feat: init lf project

This commit is contained in:
gelik 2023-05-16 07:01:55 +03:00
parent 9526f3e32f
commit 35541bb9a3
63 changed files with 702 additions and 4030 deletions

@ -8,7 +8,8 @@ help: ## show this help
install: ## install all go dependencies
go install \
github.com/vektra/mockery/v2@v2.26.0
github.com/vektra/mockery/v2@v2.26.0 \
github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4
test: ## run all layers tests

@ -11,8 +11,8 @@ import (
formatter "github.com/antonfisher/nested-logrus-formatter"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/app"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/app"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
)
func main() {

20
go.mod

@ -1,44 +1,49 @@
module penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth
module penahub.gitlab.yandexcloud.net/pena-services/customer
go 1.20
require (
github.com/SevereCloud/vksdk/v2 v2.16.0
github.com/antonfisher/nested-logrus-formatter v1.3.1
github.com/deepmap/oapi-codegen v1.12.4
github.com/getkin/kin-openapi v0.116.0
github.com/go-resty/resty/v2 v2.7.0
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/joho/godotenv v1.5.1
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/echo/v4 v4.10.2
github.com/sethvargo/go-envconfig v0.9.0
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.2
github.com/walkerus/go-wiremock v1.5.0
go.mongodb.org/mongo-driver v1.11.4
golang.org/x/oauth2 v0.6.0
)
require (
cloud.google.com/go/compute/metadata v0.2.0 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // 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/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
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/invopop/yaml v0.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
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/stretchr/objx v0.5.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
@ -52,5 +57,6 @@ require (
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
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

57
go.sum

@ -1,15 +1,28 @@
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/SevereCloud/vksdk/v2 v2.16.0 h1:DQ90qqwY/yF1X/SWZQs1kQ/Ik+tphK82d+S6Rch46wQ=
github.com/SevereCloud/vksdk/v2 v2.16.0/go.mod h1:VN6BH9nFUXcP7Uf0uX74Aht2DQ7+139aG3/Og+jia4w=
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=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s=
github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas=
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.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
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-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=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
@ -27,9 +40,15 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
@ -41,12 +60,15 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@ -54,8 +76,13 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -67,29 +94,29 @@ github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMf
github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
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/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=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/walkerus/go-wiremock v1.5.0 h1:ipaYzaZnnOJRQS4wNFqz4YFphC/sM9GM+EiLEzv3KLc=
github.com/walkerus/go-wiremock v1.5.0/go.mod h1:gMzQpReT5mG5T/PaW8pSFiPhazrcHb1mnf6JHdKwY5w=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
@ -144,9 +171,15 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -6,11 +6,11 @@ import (
"time"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/server"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/closer"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/server"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/closer"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
)
const (

@ -1,43 +0,0 @@
package client
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/client"
)
type AmocrmClientDeps struct {
Logger *logrus.Logger
URLs *models.AmocrmURL
}
type AmocrmClient struct {
logger *logrus.Logger
urls *models.AmocrmURL
}
func NewAmocrmClient(deps *AmocrmClientDeps) *AmocrmClient {
return &AmocrmClient{
logger: deps.Logger,
urls: deps.URLs,
}
}
func (receiver *AmocrmClient) GetUserInformation(ctx context.Context, accessToken string) (*models.AmocrmUserInformation, error) {
response, err := client.Get[models.AmocrmUserInformation, any](ctx, &client.RequestSettings{
URL: receiver.urls.UserInfo,
Headers: map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", accessToken),
"Content-Type": "application/json",
},
})
if err != nil {
receiver.logger.Errorf("failed to get user information on <GetUserInformation> of <AmocrmClient>: %v", err)
return nil, err
}
return response.Body, nil
}

@ -5,10 +5,10 @@ import (
"net/url"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client"
)
type AuthClientDeps struct {
@ -49,42 +49,3 @@ func (receiver *AuthClient) GetUser(ctx context.Context, userID string) (*models
return response.Body, nil
}
func (receiver *AuthClient) Register(ctx context.Context, request *models.RegisterRequest) (*models.Tokens, error) {
response, err := client.Post[models.Tokens, models.FastifyError](ctx, &client.RequestSettings{
URL: receiver.urls.Register,
Headers: map[string]string{"Content-Type": "application/json"},
Body: request,
})
if err != nil {
receiver.logger.Errorf("failed to register user on <Register> of <AuthClient>: %v", err)
return nil, err
}
if response.Error != nil {
receiver.logger.Errorf("failed request on <Register> of <AuthClient>: %s", response.Error.Message)
return nil, utils.DetermineClientErrorResponse(response.StatusCode)
}
return response.Body, nil
}
func (receiver *AuthClient) Exchange(ctx context.Context, userID, signature string) (*models.Tokens, error) {
response, err := client.Post[models.Tokens, models.FastifyError](ctx, &client.RequestSettings{
URL: receiver.urls.Exchange,
Headers: map[string]string{"Content-Type": "application/json"},
Body: models.ExchangeRequest{
UserID: userID,
Signature: signature,
},
})
if err != nil {
receiver.logger.Errorf("failed to exchange code on <Exchange> of <AuthClient>: %v", err)
return nil, err
}
if response.Error != nil {
receiver.logger.Errorf("failed request on <Exchange> of <AuthClient>: %s", response.Error.Message)
return nil, utils.DetermineClientErrorResponse(response.StatusCode)
}
return response.Body, nil
}

@ -1,43 +0,0 @@
package client
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/client"
)
type GoogleClientDeps struct {
Logger *logrus.Logger
URLs *models.GoogleURL
}
type GoogleClient struct {
logger *logrus.Logger
urls *models.GoogleURL
}
func NewGoogleClient(deps *GoogleClientDeps) *GoogleClient {
return &GoogleClient{
logger: deps.Logger,
urls: deps.URLs,
}
}
func (receiver *GoogleClient) GetUserInformation(ctx context.Context, accessToken string) (*models.GoogleUserInformation, error) {
response, err := client.Get[models.GoogleUserInformation, any](ctx, &client.RequestSettings{
URL: receiver.urls.UserInfo,
Headers: map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", accessToken),
"Content-Type": "application/json",
},
})
if err != nil {
receiver.logger.Errorf("failed to get user information on <GetUserInformation> of <GoogleClient>: %v", err)
return nil, err
}
return response.Body, nil
}

@ -1,68 +0,0 @@
package client
import (
"context"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/client"
)
type OAuthClientDeps struct {
Logger *logrus.Logger
Config *oauth2.Config
}
type OAuthClient struct {
logger *logrus.Logger
config *oauth2.Config
}
func NewOAuthClient(deps *OAuthClientDeps) *OAuthClient {
return &OAuthClient{
logger: deps.Logger,
config: deps.Config,
}
}
func (receiver *OAuthClient) Exchange(ctx context.Context, code string, options ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
if receiver.config.Endpoint.AuthStyle < 4 {
return receiver.config.Exchange(ctx, code, options...)
}
if receiver.config.Endpoint.AuthStyle == models.BodyAuthStyle {
return receiver.bodyExchange(ctx, code)
}
return nil, nil
}
func (receiver *OAuthClient) AuthCodeURL(state string, options ...oauth2.AuthCodeOption) string {
return receiver.config.AuthCodeURL(state, options...)
}
func (receiver *OAuthClient) bodyExchange(ctx context.Context, code string) (*oauth2.Token, error) {
response, err := client.Post[oauth2.Token, any](ctx, &client.RequestSettings{
URL: receiver.config.Endpoint.TokenURL,
Headers: map[string]string{"Content-Type": "application/json"},
Body: map[string]string{
"grant_type": "authorization_code",
"redirect_uri": receiver.config.RedirectURL,
"client_id": receiver.config.ClientID,
"client_secret": receiver.config.ClientSecret,
"code": code,
},
})
if err != nil {
receiver.logger.Errorf("failed to exchange code on <bodyExchange> of <OAuthClient>: %v", err)
return nil, err
}
if response.Error != nil {
receiver.logger.Errorf("failed request on <bodyExchange> of <OAuthClient>: %s", receiver.config.Endpoint.TokenURL)
return nil, utils.DetermineClientErrorResponse(response.StatusCode)
}
return response.Body, nil
}

@ -1,34 +0,0 @@
package client
import (
"github.com/SevereCloud/vksdk/v2/api"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
type VKClient struct {
logger *logrus.Logger
}
func NewVKClient(logger *logrus.Logger) *VKClient {
return &VKClient{logger: logger}
}
func (receiver *VKClient) GetUserInformation(token *oauth2.Token) (*models.VKUserInformation, error) {
vk := api.NewVK(token.AccessToken)
userInformations := []models.VKUserInformation{}
if err := vk.RequestUnmarshal("users.get", &userInformations, api.Params{
"fields": "id,photo_400_orig,sex,domain,screen_name,bdate,photo_id,followers_count,home_town,timezone,mobile_phone",
}); err != nil {
receiver.logger.Errorln("request error: ", err.Error())
return nil, err
}
currentUserInformation := userInformations[0]
currentUserInformation.Email = token.Extra("email").(string)
return &currentUserInformation, nil
}

@ -1,99 +0,0 @@
package amocrm
import (
"context"
"fmt"
"net/http"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/utils"
)
//go:generate mockery --name amocrmService
type amocrmService interface {
Auth(ctx context.Context, code string) (*models.Tokens, error)
Link(ctx context.Context, code, accessToken string) (bool, error)
}
//go:generate mockery --name oauthService
type oauthService interface {
GenerateAuthURL() string
GenerateLinkURL(accessToken string) string
ValidateState(state string) bool
}
type Deps struct {
Logger *logrus.Logger
AmocrmService amocrmService
OAuthService oauthService
}
type Controller struct {
logger *logrus.Logger
amocrmService amocrmService
oauthService oauthService
}
func New(deps *Deps) *Controller {
return &Controller{
logger: deps.Logger,
oauthService: deps.OAuthService,
amocrmService: deps.AmocrmService,
}
}
func (receiver *Controller) RedirectAuthURL(ctx echo.Context) error {
return ctx.Redirect(http.StatusTemporaryRedirect, receiver.oauthService.GenerateAuthURL())
}
func (receiver *Controller) GenerateAuthURL(ctx echo.Context) error {
return ctx.JSON(http.StatusOK, models.GenerateURLResponse{
URL: receiver.oauthService.GenerateAuthURL(),
})
}
func (receiver *Controller) RedirectLinkAccountURL(ctx echo.Context) error {
accessToken := ctx.QueryParam("accessToken")
return ctx.Redirect(http.StatusTemporaryRedirect, receiver.oauthService.GenerateLinkURL(accessToken))
}
func (receiver *Controller) GenerateLinkAccountURL(ctx echo.Context) error {
accessToken := ctx.QueryParam("accessToken")
return ctx.JSON(http.StatusOK, models.GenerateURLResponse{
URL: receiver.oauthService.GenerateLinkURL(accessToken),
})
}
func (receiver *Controller) Callback(ctx echo.Context) error {
callbackState := ctx.QueryParam("state")
callbackCode := ctx.QueryParam("code")
callbackAccessToken := ctx.QueryParam("accessToken")
if !receiver.oauthService.ValidateState(callbackState) {
receiver.logger.Errorln("state is not valid on <Callback> of <AmocrmController>")
return utils.DetermineEchoErrorResponse(ctx, errors.ErrInvalidArgs, "state is not valid")
}
if callbackAccessToken != "" {
tokens, err := receiver.amocrmService.Link(ctx.Request().Context(), callbackCode, callbackAccessToken)
if err != nil {
receiver.logger.Errorf("failed to link amocrm account on <Callback> of <AmocrmAuthController>: %v", err)
return utils.DetermineEchoErrorResponse(ctx, err, fmt.Sprintf("failed to link amocrm account: %v", err))
}
return ctx.JSON(http.StatusOK, tokens)
}
tokens, err := receiver.amocrmService.Auth(ctx.Request().Context(), callbackCode)
if err != nil {
receiver.logger.Errorf("failed to amocrm auth on <Callback> of <AmocrmAuthController>: %v", err)
return utils.DetermineEchoErrorResponse(ctx, err, fmt.Sprintf("failed to auth: %v", err))
}
return ctx.JSON(http.StatusOK, tokens)
}

@ -1,324 +0,0 @@
package amocrm_test
import (
"bytes"
"errors"
"fmt"
"net/http"
"net/url"
"testing"
"github.com/labstack/echo"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/controller/amocrm"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/controller/amocrm/mocks"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/json"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/testifyhelper"
)
func TestAmocrmCallback(t *testing.T) {
code := "testCode"
accessToken := "accessttttoken"
testifyHelper := testifyhelper.NewEchoTestifyHelper()
tokens := models.Tokens{
AccessToken: "access-token",
RefreshToken: "refresh-token",
}
t.Run("Неверный state", func(t *testing.T) {
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
OAuthService: oauthService,
})
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
Headers: map[string]string{echo.HeaderContentType: echo.MIMEApplicationJSON},
QueryParams: map[string]string{
"state": "invalid_state",
"code": code,
},
})
oauthService.EXPECT().ValidateState("invalid_state").Return(false).Once()
assert.NoError(t, amocrmAuthController.Callback(preparedRequest.EchoContext))
assert.Equal(t, http.StatusBadRequest, preparedRequest.Recorder.Code)
})
t.Run("Сервис вернул ошибку (accessToken отсутствует)", func(t *testing.T) {
amocrmService := mocks.NewAmocrmService(t)
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AmocrmService: amocrmService,
OAuthService: oauthService,
})
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
Headers: map[string]string{echo.HeaderContentType: echo.MIMEApplicationJSON},
QueryParams: map[string]string{
"state": "random_state",
"code": code,
},
})
oauthService.EXPECT().ValidateState("random_state").Return(true).Once()
amocrmService.EXPECT().Auth(mock.Anything, code).Return(nil, errors.New("")).Once()
assert.NoError(t, amocrmAuthController.Callback(preparedRequest.EchoContext))
assert.Equal(t, http.StatusInternalServerError, preparedRequest.Recorder.Code)
})
t.Run("Сервис вернул ошибку (accessToken имеется)", func(t *testing.T) {
amocrmService := mocks.NewAmocrmService(t)
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AmocrmService: amocrmService,
OAuthService: oauthService,
})
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
Headers: map[string]string{echo.HeaderContentType: echo.MIMEApplicationJSON},
QueryParams: map[string]string{
"state": "state",
"code": code,
"accessToken": accessToken,
},
})
oauthService.EXPECT().ValidateState("state").Return(true).Once()
amocrmService.AssertNotCalled(t, "Auth")
amocrmService.EXPECT().Link(mock.Anything, code, accessToken).Return(false, errors.New("")).Once()
assert.NoError(t, amocrmAuthController.Callback(preparedRequest.EchoContext))
assert.Equal(t, http.StatusInternalServerError, preparedRequest.Recorder.Code)
})
t.Run("Сервис успешно отработал (accessToken отсутствует)", func(t *testing.T) {
amocrmService := mocks.NewAmocrmService(t)
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AmocrmService: amocrmService,
OAuthService: oauthService,
})
jsonBuffer, err := json.EncodeBuffer(tokens)
if err != nil {
t.Errorf("failed to encode json tokens: %v", err)
}
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
Body: bytes.NewReader(jsonBuffer.Bytes()),
Headers: map[string]string{echo.HeaderContentType: echo.MIMEApplicationJSON},
QueryParams: map[string]string{
"state": "some_state",
"code": code,
},
})
oauthService.EXPECT().ValidateState("some_state").Return(true).Once()
amocrmService.EXPECT().Auth(mock.Anything, code).Return(&tokens, nil).Once()
assert.NoError(t, amocrmAuthController.Callback(preparedRequest.EchoContext))
assert.Equal(t, http.StatusOK, preparedRequest.Recorder.Code)
})
t.Run("Сервис успешно отработал (accessToken имеется)", func(t *testing.T) {
amocrmService := mocks.NewAmocrmService(t)
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AmocrmService: amocrmService,
OAuthService: oauthService,
})
jsonBuffer, err := json.EncodeBuffer(tokens)
if err != nil {
t.Errorf("failed to encode json tokens: %v", err)
}
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
Body: bytes.NewReader(jsonBuffer.Bytes()),
Headers: map[string]string{echo.HeaderContentType: echo.MIMEApplicationJSON},
QueryParams: map[string]string{
"state": "login_state",
"code": code,
"accessToken": accessToken,
},
})
oauthService.EXPECT().ValidateState("login_state").Return(true).Once()
amocrmService.AssertNotCalled(t, "Auth")
amocrmService.EXPECT().Link(mock.Anything, code, accessToken).Return(true, nil).Once()
assert.NoError(t, amocrmAuthController.Callback(preparedRequest.EchoContext))
assert.Equal(t, http.StatusOK, preparedRequest.Recorder.Code)
})
}
func TestAmocrmGenerateAuthURL(t *testing.T) {
testifyHelper := testifyhelper.NewEchoTestifyHelper()
t.Run("Успешная генерация ссылки авторизации", func(t *testing.T) {
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
OAuthService: oauthService,
})
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
})
url := url.URL{
Scheme: "https",
Host: "www.amocrm.ru",
Path: "/oauth",
RawQuery: "access_type=offline&client_id=&response_type=code&state=555",
}
oauthService.EXPECT().GenerateAuthURL().Return(url.String()).Once()
assert.NoError(t, amocrmAuthController.GenerateAuthURL(preparedRequest.EchoContext))
unmarsled, err := json.Unmarshal[models.GenerateURLResponse](preparedRequest.Recorder.Body.Bytes())
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, preparedRequest.Recorder.Code)
assert.EqualValues(t, url.String(), unmarsled.URL)
})
}
func TestAmocrmRedirectAuthURL(t *testing.T) {
testifyHelper := testifyhelper.NewEchoTestifyHelper()
t.Run("Успешная генерация ссылки авторизации", func(t *testing.T) {
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
OAuthService: oauthService,
})
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
})
url := url.URL{
Scheme: "https",
Host: "www.amocrm.ru",
Path: "/oauth",
RawQuery: "access_type=offline&client_id=&response_type=code&state=555",
}
oauthService.EXPECT().GenerateAuthURL().Return(url.String()).Once()
assert.NoError(t, amocrmAuthController.RedirectAuthURL(preparedRequest.EchoContext))
assert.Equal(t, http.StatusTemporaryRedirect, preparedRequest.Recorder.Code)
result := preparedRequest.Recorder.Result()
if result != nil {
defer result.Body.Close()
}
if isNotNil := assert.NotNil(t, result); isNotNil {
redirectURL, err := result.Location()
assert.NoError(t, err)
assert.Equal(t, &url, redirectURL)
}
})
}
func TestAmocrmRedirectLinkAccountURL(t *testing.T) {
testifyHelper := testifyhelper.NewEchoTestifyHelper()
accessToken := "access-tokenasg"
redirectURL := url.URL{
Scheme: "https",
Host: "www.amocrm.ru",
Path: "/oauth",
RawQuery: fmt.Sprintf(
"accessToken=%s&access_type=offline&client_id=&response_type=code&state=555",
accessToken,
),
}
t.Run("Успешная генерация ссылки авторизации с токеном доступа", func(t *testing.T) {
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
OAuthService: oauthService,
})
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
QueryParams: map[string]string{"accessToken": accessToken},
})
oauthService.EXPECT().GenerateLinkURL(accessToken).Return(redirectURL.String()).Once()
assert.NoError(t, amocrmAuthController.RedirectLinkAccountURL(preparedRequest.EchoContext))
assert.Equal(t, http.StatusTemporaryRedirect, preparedRequest.Recorder.Code)
result := preparedRequest.Recorder.Result()
if result != nil {
defer result.Body.Close()
}
if isNotNil := assert.NotNil(t, result); isNotNil {
redirectURL, err := result.Location()
assert.NoError(t, err)
assert.Equal(t, redirectURL, redirectURL)
}
})
}
func TestAmocrmGenerateLinkAccountURL(t *testing.T) {
testifyHelper := testifyhelper.NewEchoTestifyHelper()
accessToken := "access-token"
redirectURL := url.URL{
Scheme: "https",
Host: "www.amocrm.ru",
Path: "/oauth",
RawQuery: fmt.Sprintf(
"accessToken=%s&access_type=offline&client_id=&response_type=code&state=555",
accessToken,
),
}
t.Run("Успешная генерация ссылки авторизации с токеном доступа", func(t *testing.T) {
oauthService := mocks.NewOauthService(t)
amocrmAuthController := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
OAuthService: oauthService,
})
preparedRequest := testifyHelper.PrepareRequest(testifyhelper.RequestConfiguration{
Method: http.MethodGet,
QueryParams: map[string]string{"accessToken": accessToken},
})
oauthService.EXPECT().GenerateLinkURL(accessToken).Return(redirectURL.String()).Once()
assert.NoError(t, amocrmAuthController.GenerateLinkAccountURL(preparedRequest.EchoContext))
assert.Equal(t, http.StatusOK, preparedRequest.Recorder.Code)
unmarsled, err := json.Unmarshal[models.GenerateURLResponse](preparedRequest.Recorder.Body.Bytes())
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, preparedRequest.Recorder.Code)
assert.EqualValues(t, redirectURL.String(), unmarsled.URL)
})
}

@ -1,147 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
models "penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
// AmocrmService is an autogenerated mock type for the amocrmService type
type AmocrmService struct {
mock.Mock
}
type AmocrmService_Expecter struct {
mock *mock.Mock
}
func (_m *AmocrmService) EXPECT() *AmocrmService_Expecter {
return &AmocrmService_Expecter{mock: &_m.Mock}
}
// Auth provides a mock function with given fields: ctx, code
func (_m *AmocrmService) Auth(ctx context.Context, code string) (*models.Tokens, error) {
ret := _m.Called(ctx, code)
var r0 *models.Tokens
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.Tokens, error)); ok {
return rf(ctx, code)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.Tokens); ok {
r0 = rf(ctx, code)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Tokens)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, code)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AmocrmService_Auth_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Auth'
type AmocrmService_Auth_Call struct {
*mock.Call
}
// Auth is a helper method to define mock.On call
// - ctx context.Context
// - code string
func (_e *AmocrmService_Expecter) Auth(ctx interface{}, code interface{}) *AmocrmService_Auth_Call {
return &AmocrmService_Auth_Call{Call: _e.mock.On("Auth", ctx, code)}
}
func (_c *AmocrmService_Auth_Call) Run(run func(ctx context.Context, code string)) *AmocrmService_Auth_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *AmocrmService_Auth_Call) Return(_a0 *models.Tokens, _a1 error) *AmocrmService_Auth_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AmocrmService_Auth_Call) RunAndReturn(run func(context.Context, string) (*models.Tokens, error)) *AmocrmService_Auth_Call {
_c.Call.Return(run)
return _c
}
// Link provides a mock function with given fields: ctx, code, accessToken
func (_m *AmocrmService) Link(ctx context.Context, code string, accessToken string) (bool, error) {
ret := _m.Called(ctx, code, accessToken)
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) (bool, error)); ok {
return rf(ctx, code, accessToken)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string) bool); ok {
r0 = rf(ctx, code, accessToken)
} else {
r0 = ret.Get(0).(bool)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = rf(ctx, code, accessToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AmocrmService_Link_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Link'
type AmocrmService_Link_Call struct {
*mock.Call
}
// Link is a helper method to define mock.On call
// - ctx context.Context
// - code string
// - accessToken string
func (_e *AmocrmService_Expecter) Link(ctx interface{}, code interface{}, accessToken interface{}) *AmocrmService_Link_Call {
return &AmocrmService_Link_Call{Call: _e.mock.On("Link", ctx, code, accessToken)}
}
func (_c *AmocrmService_Link_Call) Run(run func(ctx context.Context, code string, accessToken string)) *AmocrmService_Link_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string), args[2].(string))
})
return _c
}
func (_c *AmocrmService_Link_Call) Return(_a0 bool, _a1 error) *AmocrmService_Link_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AmocrmService_Link_Call) RunAndReturn(run func(context.Context, string, string) (bool, error)) *AmocrmService_Link_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewAmocrmService interface {
mock.TestingT
Cleanup(func())
}
// NewAmocrmService creates a new instance of AmocrmService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewAmocrmService(t mockConstructorTestingTNewAmocrmService) *AmocrmService {
mock := &AmocrmService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,158 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// OauthService is an autogenerated mock type for the oauthService type
type OauthService struct {
mock.Mock
}
type OauthService_Expecter struct {
mock *mock.Mock
}
func (_m *OauthService) EXPECT() *OauthService_Expecter {
return &OauthService_Expecter{mock: &_m.Mock}
}
// GenerateAuthURL provides a mock function with given fields:
func (_m *OauthService) GenerateAuthURL() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// OauthService_GenerateAuthURL_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateAuthURL'
type OauthService_GenerateAuthURL_Call struct {
*mock.Call
}
// GenerateAuthURL is a helper method to define mock.On call
func (_e *OauthService_Expecter) GenerateAuthURL() *OauthService_GenerateAuthURL_Call {
return &OauthService_GenerateAuthURL_Call{Call: _e.mock.On("GenerateAuthURL")}
}
func (_c *OauthService_GenerateAuthURL_Call) Run(run func()) *OauthService_GenerateAuthURL_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *OauthService_GenerateAuthURL_Call) Return(_a0 string) *OauthService_GenerateAuthURL_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *OauthService_GenerateAuthURL_Call) RunAndReturn(run func() string) *OauthService_GenerateAuthURL_Call {
_c.Call.Return(run)
return _c
}
// GenerateLinkURL provides a mock function with given fields: accessToken
func (_m *OauthService) GenerateLinkURL(accessToken string) string {
ret := _m.Called(accessToken)
var r0 string
if rf, ok := ret.Get(0).(func(string) string); ok {
r0 = rf(accessToken)
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// OauthService_GenerateLinkURL_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateLinkURL'
type OauthService_GenerateLinkURL_Call struct {
*mock.Call
}
// GenerateLinkURL is a helper method to define mock.On call
// - accessToken string
func (_e *OauthService_Expecter) GenerateLinkURL(accessToken interface{}) *OauthService_GenerateLinkURL_Call {
return &OauthService_GenerateLinkURL_Call{Call: _e.mock.On("GenerateLinkURL", accessToken)}
}
func (_c *OauthService_GenerateLinkURL_Call) Run(run func(accessToken string)) *OauthService_GenerateLinkURL_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *OauthService_GenerateLinkURL_Call) Return(_a0 string) *OauthService_GenerateLinkURL_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *OauthService_GenerateLinkURL_Call) RunAndReturn(run func(string) string) *OauthService_GenerateLinkURL_Call {
_c.Call.Return(run)
return _c
}
// ValidateState provides a mock function with given fields: state
func (_m *OauthService) ValidateState(state string) bool {
ret := _m.Called(state)
var r0 bool
if rf, ok := ret.Get(0).(func(string) bool); ok {
r0 = rf(state)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// OauthService_ValidateState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateState'
type OauthService_ValidateState_Call struct {
*mock.Call
}
// ValidateState is a helper method to define mock.On call
// - state string
func (_e *OauthService_Expecter) ValidateState(state interface{}) *OauthService_ValidateState_Call {
return &OauthService_ValidateState_Call{Call: _e.mock.On("ValidateState", state)}
}
func (_c *OauthService_ValidateState_Call) Run(run func(state string)) *OauthService_ValidateState_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *OauthService_ValidateState_Call) Return(_a0 bool) *OauthService_ValidateState_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *OauthService_ValidateState_Call) RunAndReturn(run func(string) bool) *OauthService_ValidateState_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewOauthService interface {
mock.TestingT
Cleanup(func())
}
// NewOauthService creates a new instance of OauthService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewOauthService(t mockConstructorTestingTNewOauthService) *OauthService {
mock := &OauthService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,75 +0,0 @@
package google
import (
"context"
"net/http"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/utils"
)
// TODO:
// 1) Необходимо вынести все ошибки в отдельный пакет
// 2) Реализовать map'у для возврата JSON ошибок
type GoogleClient interface {
GetUserInformation(ctx context.Context, accessToken string) (*models.GoogleUserInformation, error)
}
type Deps struct {
GoogleOAuthConfig *oauth2.Config
Client GoogleClient
Logger *logrus.Logger
}
type Controller struct {
oAuth *oauth2.Config
logger *logrus.Logger
client GoogleClient
state string
}
func New(deps *Deps) *Controller {
return &Controller{
oAuth: deps.GoogleOAuthConfig,
logger: deps.Logger,
client: deps.Client,
state: utils.GetRandomString(10),
}
}
func (receiver *Controller) Auth(ctx echo.Context) error {
url := receiver.oAuth.AuthCodeURL(receiver.state, oauth2.AccessTypeOffline)
return ctx.Redirect(http.StatusTemporaryRedirect, url)
}
func (receiver *Controller) Callback(ctx echo.Context) error {
callbackState := ctx.FormValue("state")
callbackCode := ctx.FormValue("code")
if callbackState != receiver.state {
receiver.logger.Errorln("state is not valid")
return ctx.JSON(http.StatusBadRequest, "state is not valid")
}
token, err := receiver.oAuth.Exchange(ctx.Request().Context(), callbackCode)
if err != nil {
receiver.logger.Errorln("exchange error: ", err.Error())
return ctx.JSON(http.StatusBadRequest, err.Error())
}
userInformation, err := receiver.client.GetUserInformation(ctx.Request().Context(), token.AccessToken)
if err != nil {
receiver.logger.Errorln("get user information error: ", err.Error())
return ctx.JSON(http.StatusInternalServerError, err.Error())
}
return ctx.JSON(http.StatusOK, userInformation)
}

@ -1,71 +0,0 @@
package vk
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/utils"
)
type VKClient interface {
GetUserInformation(token *oauth2.Token) (*models.VKUserInformation, error)
}
type Deps struct {
VKOAuthConfig *oauth2.Config
Client VKClient
Logger *logrus.Logger
}
type Controller struct {
oAuth *oauth2.Config
logger *logrus.Logger
client VKClient
state string
}
func New(deps *Deps) *Controller {
return &Controller{
oAuth: deps.VKOAuthConfig,
logger: deps.Logger,
client: deps.Client,
state: utils.GetRandomString(10),
}
}
func (receiver *Controller) Auth(ctx echo.Context) error {
url := receiver.oAuth.AuthCodeURL(receiver.state, oauth2.AccessTypeOffline)
return ctx.Redirect(http.StatusTemporaryRedirect, url)
}
func (receiver *Controller) Callback(ctx echo.Context) error {
queries := ctx.Request().URL.Query()
callbackCode := queries.Get("code")
callbackState := queries.Get("state")
if callbackState != receiver.state {
receiver.logger.Errorln("state is not valid on <Callback> of <AmocrmController>")
return ctx.JSON(http.StatusBadRequest, "state is not valid")
}
token, err := receiver.oAuth.Exchange(ctx.Request().Context(), callbackCode)
if err != nil {
receiver.logger.Errorf("exchange error: %v", err)
return ctx.JSON(http.StatusBadRequest, err.Error())
}
userInformation, err := receiver.client.GetUserInformation(token)
if err != nil {
receiver.logger.Errorf("get user information error: %v", err)
return ctx.JSON(http.StatusInternalServerError, err.Error())
}
return ctx.JSON(http.StatusOK, userInformation)
}

@ -3,8 +3,8 @@ package initialize
import (
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/client"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
type ClientsDeps struct {
@ -16,31 +16,14 @@ type ClientsDeps struct {
}
type Clients struct {
GoogleClient *client.GoogleClient
VKClient *client.VKClient
AmocrmClient *client.AmocrmClient
AuthClient *client.AuthClient
AmocrmOAuthClient *client.OAuthClient
AuthClient *client.AuthClient
}
func NewClients(deps *ClientsDeps) *Clients {
return &Clients{
VKClient: client.NewVKClient(deps.Logger),
GoogleClient: client.NewGoogleClient(&client.GoogleClientDeps{
Logger: deps.Logger,
URLs: deps.GoogleURL,
}),
AmocrmClient: client.NewAmocrmClient(&client.AmocrmClientDeps{
Logger: deps.Logger,
URLs: deps.AmocrmURL,
}),
AuthClient: client.NewAuthClient(&client.AuthClientDeps{
Logger: deps.Logger,
URLs: deps.AuthURL,
}),
AmocrmOAuthClient: client.NewOAuthClient(&client.OAuthClientDeps{
Logger: deps.Logger,
Config: deps.AmocrmOAuthConfiguration,
}),
}
}

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
)
func TestNewClients(t *testing.T) {
@ -13,11 +13,7 @@ func TestNewClients(t *testing.T) {
clients := initialize.NewClients(&initialize.ClientsDeps{})
assert.NotNil(t, clients)
assert.NotNil(t, clients.AmocrmClient)
assert.NotNil(t, clients.AuthClient)
assert.NotNil(t, clients.GoogleClient)
assert.NotNil(t, clients.AmocrmOAuthClient)
assert.NotNil(t, clients.VKClient)
})
})
}

@ -7,9 +7,9 @@ import (
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/vk"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/env"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/env"
)
func Configuration(path string) (*models.Config, error) {

@ -9,9 +9,9 @@ import (
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/vk"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
)
func setDefaultTestingENV(t *testing.T) *models.Config {

@ -2,9 +2,6 @@ package initialize
import (
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/controller/amocrm"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/controller/google"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/controller/vk"
)
type ControllersDeps struct {
@ -13,19 +10,8 @@ type ControllersDeps struct {
}
type Controllers struct {
GoogleController *google.Controller
VKController *vk.Controller
AmocrmController *amocrm.Controller
}
func NewControllers(deps *ControllersDeps) *Controllers {
return &Controllers{
AmocrmController: amocrm.New(&amocrm.Deps{
Logger: deps.Logger,
OAuthService: deps.Services.AmocrmOAuthService,
AmocrmService: deps.Services.AmocrmService,
}),
VKController: vk.New(&vk.Deps{}),
GoogleController: google.New(&google.Deps{}),
}
return &Controllers{}
}

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
)
func TestNewControllers(t *testing.T) {
@ -15,9 +15,6 @@ func TestNewControllers(t *testing.T) {
})
assert.NotNil(t, controllers)
assert.NotNil(t, controllers.AmocrmController)
assert.NotNil(t, controllers.GoogleController)
assert.NotNil(t, controllers.VKController)
})
})
}

@ -3,7 +3,7 @@ package initialize
import (
"github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/repository"
)
type RepositoriesDeps struct {

@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
)
func TestNewRepositories(t *testing.T) {

@ -2,12 +2,7 @@ package initialize
import (
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/amocrm"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/auth"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/encrypt"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/oauth"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
type ServicesDeps struct {
@ -18,34 +13,8 @@ type ServicesDeps struct {
}
type Services struct {
AmocrmService *amocrm.Service
AmocrmOAuthService *oauth.Service[models.AmocrmUserInformation]
}
func NewServices(deps *ServicesDeps) *Services {
authService := auth.New(&auth.Deps{
Logger: deps.Logger,
AuthClient: deps.Clients.AuthClient,
EncryptionService: encrypt.New(&encrypt.ServiceDeps{
JWT: utils.NewJWT[models.JWTAuthUser](&deps.Config.JWT),
PrivateCurveKey: deps.Config.AuthMicroservice.PrivateSignKey,
SignSecret: deps.Config.AuthMicroservice.AuthGroup,
}),
})
amocrmOAuthService := oauth.New(&oauth.Deps[models.AmocrmUserInformation]{
Logger: deps.Logger,
ServiceClient: deps.Clients.AmocrmClient,
OAuthClient: deps.Clients.AmocrmOAuthClient,
})
return &Services{
AmocrmOAuthService: amocrmOAuthService,
AmocrmService: amocrm.New(&amocrm.Deps{
Logger: deps.Logger,
AuthService: authService,
Repository: deps.Repositories.AmocrmRepository,
OAuthService: amocrmOAuthService,
}),
}
return &Services{}
}

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
)
func TestNewServices(t *testing.T) {
@ -19,8 +19,6 @@ func TestNewServices(t *testing.T) {
})
assert.NotNil(t, services)
assert.NotNil(t, services.AmocrmOAuthService)
assert.NotNil(t, services.AmocrmService)
})
})
}

@ -5,7 +5,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
)
type Config struct {

@ -8,10 +8,10 @@ import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/fields"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
mongoWrapper "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/mongo"
)
type AmocrmRepository struct {

@ -7,8 +7,8 @@ import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
type GoogleRepository struct {

@ -6,11 +6,14 @@ import (
"net/http"
"time"
oapiMiddleware "github.com/deepmap/oapi-codegen/pkg/middleware"
"github.com/getkin/kin-openapi/openapi3"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/swagger"
)
type HTTP struct {
@ -19,11 +22,12 @@ type HTTP struct {
echo *echo.Echo
}
func New(logger *logrus.Logger) *HTTP {
func New(logger *logrus.Logger, swagger *openapi3.T) *HTTP {
echo := echo.New()
echo.Use(middleware.Logger())
echo.Use(middleware.Recover())
echo.Use(oapiMiddleware.OapiRequestValidator(swagger))
return &HTTP{
echo: echo,
@ -65,13 +69,7 @@ func (receiver *HTTP) Stop(ctx context.Context) error {
}
func (receiver *HTTP) Register(controllers *initialize.Controllers) *HTTP {
groupAmocrm := receiver.echo.Group("/amocrm")
groupAmocrm.GET("/auth/redirect", controllers.AmocrmController.RedirectAuthURL)
groupAmocrm.GET("/auth", controllers.AmocrmController.GenerateAuthURL)
groupAmocrm.GET("/link/redirect", controllers.AmocrmController.RedirectLinkAccountURL)
groupAmocrm.GET("/link", controllers.AmocrmController.GenerateLinkAccountURL)
groupAmocrm.GET("/callback", controllers.AmocrmController.Callback)
swagger.RegisterHandlers(receiver.echo, nil)
return receiver
}

@ -1,131 +0,0 @@
package amocrm
import (
"context"
"strconv"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
//go:generate mockery --name oauthService
type oauthService[T models.AmocrmUserInformation] interface {
GetUserInformationByCode(ctx context.Context, code string) (*T, error)
}
//go:generate mockery --name authService
type authService interface {
Register(ctx context.Context, information *models.AuthUserInformation) (*models.Tokens, string, error)
Login(ctx context.Context, userID string) (*models.Tokens, error)
GetAuthUserByToken(ctx context.Context, accessToken string) (*models.AuthUser, error)
}
//go:generate mockery --name amocrmRepository
type amocrmRepository interface {
FindByID(ctx context.Context, amocrmID string) (*models.AmocrmUser, error)
FindByUserID(ctx context.Context, userID string) (*models.AmocrmUser, error)
Insert(ctx context.Context, user *models.AmocrmUser) (*models.AmocrmUser, error)
}
type Deps struct {
Logger *logrus.Logger
Repository amocrmRepository
OAuthService oauthService[models.AmocrmUserInformation]
AuthService authService
}
type Service struct {
logger *logrus.Logger
oauthService oauthService[models.AmocrmUserInformation]
authService authService
repository amocrmRepository
}
func New(deps *Deps) *Service {
return &Service{
oauthService: deps.OAuthService,
logger: deps.Logger,
authService: deps.AuthService,
repository: deps.Repository,
}
}
func (receiver *Service) Link(ctx context.Context, code, accessToken string) (bool, error) {
authUser, err := receiver.authService.GetAuthUserByToken(ctx, accessToken)
if err != nil {
receiver.logger.Errorf("failed to get auth user on <Link> of <AmocrmService>: %v", err)
return false, err
}
amocrmUserInformation, err := receiver.oauthService.GetUserInformationByCode(ctx, code)
if err != nil {
receiver.logger.Errorf("failed to get amocrm user information on <Link> of <AmocrmService>: %v", err)
return false, err
}
amocrmUserID := strconv.Itoa(int(amocrmUserInformation.ID))
amocrmUser, err := receiver.repository.FindByID(ctx, amocrmUserID)
if err != nil && err != errors.ErrNoRecord {
receiver.logger.Errorf("failed to find user by amocrm id on <Link> of <AmocrmService>: %v", err)
return false, err
}
if amocrmUser != nil {
return false, errors.ErrRecordAlreadyExist
}
if _, err := receiver.repository.Insert(ctx, &models.AmocrmUser{
UserID: authUser.ID,
AmocrmID: amocrmUserID,
Information: *amocrmUserInformation,
}); err != nil {
receiver.logger.Errorf("failed to insert amocrm user on <Link> of <AmocrmService>: %v", err)
return false, err
}
return true, nil
}
func (receiver *Service) Auth(ctx context.Context, code string) (*models.Tokens, error) {
amocrmUserInformation, err := receiver.oauthService.GetUserInformationByCode(ctx, code)
if err != nil {
receiver.logger.Errorf("failed to get amocrm user information on <Auth> of <AmocrmService>: %v", err)
return nil, err
}
amocrmUserID := strconv.Itoa(int(amocrmUserInformation.ID))
amocrmUser, err := receiver.repository.FindByID(ctx, amocrmUserID)
if err != nil && err != errors.ErrNoRecord {
receiver.logger.Errorf("failed to find amocrm user by id on <Auth> of <AmocrmService>: %v", err)
return nil, err
}
if amocrmUser == nil {
tokens, createdUserID, registerErr := receiver.authService.Register(ctx, &models.AuthUserInformation{})
if registerErr != nil {
receiver.logger.Errorf("failed to register amocrm user on <Auth> of <AmocrmService>: %v", err)
return nil, registerErr
}
if _, insertErr := receiver.repository.Insert(ctx, &models.AmocrmUser{
UserID: createdUserID,
AmocrmID: amocrmUserID,
Information: *amocrmUserInformation,
}); insertErr != nil {
receiver.logger.Errorf("failed to insert user on <Auth> of <AmocrmService>: %v", err)
return nil, insertErr
}
return tokens, nil
}
tokens, err := receiver.authService.Login(ctx, amocrmUser.UserID)
if err != nil {
receiver.logger.Errorf("failed to login amocrm user on <Auth> of <AmocrmService>: %v", err)
return nil, err
}
return tokens, nil
}

@ -1,522 +0,0 @@
package amocrm_test
import (
"context"
"strconv"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/amocrm"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/amocrm/mocks"
)
var (
inputCode = "input-code"
amocrmUserInformation = models.AmocrmUserInformation{
ID: 2,
UUID: "geagae0-13413g-gadg",
}
amocrmUser = models.AmocrmUser{
UserID: "153-35-5135",
Information: amocrmUserInformation,
}
authUser = models.AuthUser{
ID: "1geat",
Login: "gkohoeh",
}
authTokens = models.Tokens{
AccessToken: "auth-251-access-token-auth",
RefreshToken: "auth-7582-refresh-token-auth",
}
)
func TestAmocrmLink(t *testing.T) {
t.Run("Успешная привязка аккаунта amocrm", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getAuthUserCall := authService.EXPECT().
GetAuthUserByToken(mock.Anything, authTokens.AccessToken).
Return(&authUser, nil).
Once()
getUserInformationCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
findByAmocrmIDCall := amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(nil, errors.ErrNoRecord).
NotBefore(getUserInformationCall).
Once()
amocrmRepository.EXPECT().
Insert(mock.Anything, &models.AmocrmUser{
UserID: authUser.ID,
AmocrmID: strconv.Itoa(int(amocrmUserInformation.ID)),
Information: amocrmUserInformation,
}).
Return(&amocrmUser, nil).
NotBefore(getAuthUserCall, findByAmocrmIDCall).
Once()
isLinked, err := amocrmService.Link(context.Background(), inputCode, authTokens.AccessToken)
assert.NoError(t, err)
assert.Equal(t, true, isLinked)
})
t.Run("Ошибка получения пользователя ЕСА по токену при привязке аккаунта amocrm", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
authService.EXPECT().
GetAuthUserByToken(mock.Anything, authTokens.AccessToken).
Return(&authUser, errors.ErrInvalidArgs).
Once()
oauthService.AssertNotCalled(t, "GetUserInformationByCode")
amocrmRepository.AssertNotCalled(t, "FindByID")
amocrmRepository.AssertNotCalled(t, "Insert")
isLinked, err := amocrmService.Link(context.Background(), inputCode, authTokens.AccessToken)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrInvalidArgs.Error())
assert.Equal(t, false, isLinked)
})
t.Run("Ошибка получения пользователя amocrm по коду при привязке аккаунта amocrm", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
authService.EXPECT().
GetAuthUserByToken(mock.Anything, authTokens.AccessToken).
Return(&authUser, nil).
Once()
oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(nil, errors.ErrInvalidArgs).
Once()
amocrmRepository.AssertNotCalled(t, "FindByID")
amocrmRepository.AssertNotCalled(t, "Insert")
isLinked, err := amocrmService.Link(context.Background(), inputCode, authTokens.AccessToken)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrInvalidArgs.Error())
assert.Equal(t, false, isLinked)
})
t.Run("Ошибка привязки аккаунта amocrm: пользователь уже существует", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getAuthUserCall := authService.EXPECT().
GetAuthUserByToken(mock.Anything, authTokens.AccessToken).
Return(&authUser, nil).
Once()
getUserInformationCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
NotBefore(getAuthUserCall).
Once()
amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(&amocrmUser, nil).
NotBefore(getUserInformationCall).
Once()
amocrmRepository.AssertNotCalled(t, "Insert")
isLinked, err := amocrmService.Link(context.Background(), inputCode, authTokens.AccessToken)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrRecordAlreadyExist.Error())
assert.Equal(t, false, isLinked)
})
t.Run("Неизвестная ошибка при поиске пользователя amocrm в БД при привязке аккаунта", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
authService.EXPECT().
GetAuthUserByToken(mock.Anything, authTokens.AccessToken).
Return(&authUser, nil).
Once()
getUserInformationCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(nil, errors.ErrDecodeRecord).
NotBefore(getUserInformationCall).
Once()
amocrmRepository.AssertNotCalled(t, "Insert")
isLinked, err := amocrmService.Link(context.Background(), inputCode, authTokens.AccessToken)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrDecodeRecord.Error())
assert.Equal(t, false, isLinked)
})
t.Run("Непредвиденная ошибка поиска пользователя amocrm при привязке аккаунта amocrm", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getAuthUserCall := authService.EXPECT().
GetAuthUserByToken(mock.Anything, authTokens.AccessToken).
Return(&authUser, nil).
Once()
getUserInformationCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
findByAmocrmIDCall := amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(nil, errors.ErrNoRecord).
NotBefore(getUserInformationCall).
Once()
amocrmRepository.EXPECT().
Insert(mock.Anything, &models.AmocrmUser{
UserID: authUser.ID,
AmocrmID: strconv.Itoa(int(amocrmUserInformation.ID)),
Information: amocrmUserInformation,
}).
Return(nil, errors.ErrInsertRecord).
NotBefore(getAuthUserCall, findByAmocrmIDCall).
Once()
isLinked, err := amocrmService.Link(context.Background(), inputCode, authTokens.AccessToken)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrInsertRecord.Error())
assert.Equal(t, false, isLinked)
})
}
func TestAmocrmAuth(t *testing.T) {
t.Run("Успешная авторизация аккаунта через amocrm (пользователь отсутствует в системе)", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getUserInfoByCodeCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
findByIDCall := amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(nil, errors.ErrNoRecord).
NotBefore(getUserInfoByCodeCall).
Once()
registerCall := authService.EXPECT().
Register(mock.Anything, &models.AuthUserInformation{}).
Return(&authTokens, authUser.ID, nil).
NotBefore(findByIDCall).
Once()
amocrmRepository.EXPECT().
Insert(mock.Anything, &models.AmocrmUser{
UserID: authUser.ID,
AmocrmID: strconv.Itoa(int(amocrmUserInformation.ID)),
Information: amocrmUserInformation,
}).
Return(&amocrmUser, nil).
NotBefore(registerCall).
Once()
authService.AssertNotCalled(t, "Login")
tokens, err := amocrmService.Auth(context.Background(), inputCode)
assert.NoError(t, err)
assert.Equal(t, &authTokens, tokens)
})
t.Run("Успешная авторизация аккаунта через amocrm (пользователь присутствует в системе)", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getUserInfoByCodeCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
findByIDCall := amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(&amocrmUser, nil).
NotBefore(getUserInfoByCodeCall).
Once()
authService.EXPECT().
Login(mock.Anything, amocrmUser.UserID).
Return(&authTokens, nil).
NotBefore(findByIDCall).
Once()
authService.AssertNotCalled(t, "Register")
tokens, err := amocrmService.Auth(context.Background(), inputCode)
assert.NoError(t, err)
assert.Equal(t, &authTokens, tokens)
})
t.Run("Ошибка логина при авторизации через amocrm (пользователь присутствует в системе)", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getUserInfoByCodeCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(&amocrmUser, nil).
NotBefore(getUserInfoByCodeCall).
Once()
authService.EXPECT().
Login(mock.Anything, amocrmUser.UserID).
Return(nil, errors.ErrMethodNotImplemented).
Once()
authService.AssertNotCalled(t, "Register")
tokens, err := amocrmService.Auth(context.Background(), inputCode)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrMethodNotImplemented.Error())
assert.Nil(t, tokens)
})
t.Run("Ошибка добавления пользователя amocrm в БД", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getUserInfoByCodeCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(nil, errors.ErrNoRecord).
NotBefore(getUserInfoByCodeCall).
Once()
authService.EXPECT().
Register(mock.Anything, &models.AuthUserInformation{}).
Return(&authTokens, authUser.ID, nil).
Once()
amocrmRepository.EXPECT().
Insert(mock.Anything, &models.AmocrmUser{
UserID: authUser.ID,
AmocrmID: strconv.Itoa(int(amocrmUserInformation.ID)),
Information: amocrmUserInformation,
}).
Return(nil, errors.ErrInsertRecord).
Once()
authService.AssertNotCalled(t, "Login")
tokens, err := amocrmService.Auth(context.Background(), inputCode)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrInsertRecord.Error())
assert.Nil(t, tokens)
})
t.Run("Ошибка регистрации пользователя amocrm", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getUserInfoByCodeCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(nil, errors.ErrNoRecord).
NotBefore(getUserInfoByCodeCall).
Once()
authService.EXPECT().
Register(mock.Anything, &models.AuthUserInformation{}).
Return(nil, "", errors.ErrMethodNotImplemented).
Once()
amocrmRepository.AssertNotCalled(t, "Insert")
authService.AssertNotCalled(t, "Login")
tokens, err := amocrmService.Auth(context.Background(), inputCode)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrMethodNotImplemented.Error())
assert.Nil(t, tokens)
})
t.Run("Ошибка получения пользователя amocrm при авторизации через аккаунт", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
getUserInfoByCodeCall := oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(&amocrmUserInformation, nil).
Once()
amocrmRepository.EXPECT().
FindByID(mock.Anything, strconv.Itoa(int(amocrmUserInformation.ID))).
Return(nil, errors.ErrFindRecord).
NotBefore(getUserInfoByCodeCall).
Once()
amocrmRepository.AssertNotCalled(t, "Insert")
authService.AssertNotCalled(t, "Login")
authService.AssertNotCalled(t, "Register")
tokens, err := amocrmService.Auth(context.Background(), inputCode)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrFindRecord.Error())
assert.Nil(t, tokens)
})
t.Run("Ошибка получения информации пользователя amocrm при авторизации через аккаунт", func(t *testing.T) {
oauthService := mocks.NewOauthService[models.AmocrmUserInformation](t)
authService := mocks.NewAuthService(t)
amocrmRepository := mocks.NewAmocrmRepository(t)
amocrmService := amocrm.New(&amocrm.Deps{
Logger: logrus.New(),
AuthService: authService,
OAuthService: oauthService,
Repository: amocrmRepository,
})
oauthService.EXPECT().
GetUserInformationByCode(mock.Anything, inputCode).
Return(nil, errors.ErrMethodNotImplemented).
Once()
amocrmRepository.AssertNotCalled(t, "Insert")
amocrmRepository.AssertNotCalled(t, "FindByID")
authService.AssertNotCalled(t, "Login")
authService.AssertNotCalled(t, "Register")
tokens, err := amocrmService.Auth(context.Background(), inputCode)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrMethodNotImplemented.Error())
assert.Nil(t, tokens)
})
}

@ -1,203 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
models "penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
// AmocrmRepository is an autogenerated mock type for the amocrmRepository type
type AmocrmRepository struct {
mock.Mock
}
type AmocrmRepository_Expecter struct {
mock *mock.Mock
}
func (_m *AmocrmRepository) EXPECT() *AmocrmRepository_Expecter {
return &AmocrmRepository_Expecter{mock: &_m.Mock}
}
// FindByID provides a mock function with given fields: ctx, amocrmID
func (_m *AmocrmRepository) FindByID(ctx context.Context, amocrmID string) (*models.AmocrmUser, error) {
ret := _m.Called(ctx, amocrmID)
var r0 *models.AmocrmUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.AmocrmUser, error)); ok {
return rf(ctx, amocrmID)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.AmocrmUser); ok {
r0 = rf(ctx, amocrmID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.AmocrmUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, amocrmID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AmocrmRepository_FindByID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByID'
type AmocrmRepository_FindByID_Call struct {
*mock.Call
}
// FindByID is a helper method to define mock.On call
// - ctx context.Context
// - amocrmID string
func (_e *AmocrmRepository_Expecter) FindByID(ctx interface{}, amocrmID interface{}) *AmocrmRepository_FindByID_Call {
return &AmocrmRepository_FindByID_Call{Call: _e.mock.On("FindByID", ctx, amocrmID)}
}
func (_c *AmocrmRepository_FindByID_Call) Run(run func(ctx context.Context, amocrmID string)) *AmocrmRepository_FindByID_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *AmocrmRepository_FindByID_Call) Return(_a0 *models.AmocrmUser, _a1 error) *AmocrmRepository_FindByID_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AmocrmRepository_FindByID_Call) RunAndReturn(run func(context.Context, string) (*models.AmocrmUser, error)) *AmocrmRepository_FindByID_Call {
_c.Call.Return(run)
return _c
}
// FindByUserID provides a mock function with given fields: ctx, userID
func (_m *AmocrmRepository) FindByUserID(ctx context.Context, userID string) (*models.AmocrmUser, error) {
ret := _m.Called(ctx, userID)
var r0 *models.AmocrmUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.AmocrmUser, error)); ok {
return rf(ctx, userID)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.AmocrmUser); ok {
r0 = rf(ctx, userID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.AmocrmUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, userID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AmocrmRepository_FindByUserID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindByUserID'
type AmocrmRepository_FindByUserID_Call struct {
*mock.Call
}
// FindByUserID is a helper method to define mock.On call
// - ctx context.Context
// - userID string
func (_e *AmocrmRepository_Expecter) FindByUserID(ctx interface{}, userID interface{}) *AmocrmRepository_FindByUserID_Call {
return &AmocrmRepository_FindByUserID_Call{Call: _e.mock.On("FindByUserID", ctx, userID)}
}
func (_c *AmocrmRepository_FindByUserID_Call) Run(run func(ctx context.Context, userID string)) *AmocrmRepository_FindByUserID_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *AmocrmRepository_FindByUserID_Call) Return(_a0 *models.AmocrmUser, _a1 error) *AmocrmRepository_FindByUserID_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AmocrmRepository_FindByUserID_Call) RunAndReturn(run func(context.Context, string) (*models.AmocrmUser, error)) *AmocrmRepository_FindByUserID_Call {
_c.Call.Return(run)
return _c
}
// Insert provides a mock function with given fields: ctx, user
func (_m *AmocrmRepository) Insert(ctx context.Context, user *models.AmocrmUser) (*models.AmocrmUser, error) {
ret := _m.Called(ctx, user)
var r0 *models.AmocrmUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *models.AmocrmUser) (*models.AmocrmUser, error)); ok {
return rf(ctx, user)
}
if rf, ok := ret.Get(0).(func(context.Context, *models.AmocrmUser) *models.AmocrmUser); ok {
r0 = rf(ctx, user)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.AmocrmUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *models.AmocrmUser) error); ok {
r1 = rf(ctx, user)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AmocrmRepository_Insert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Insert'
type AmocrmRepository_Insert_Call struct {
*mock.Call
}
// Insert is a helper method to define mock.On call
// - ctx context.Context
// - user *models.AmocrmUser
func (_e *AmocrmRepository_Expecter) Insert(ctx interface{}, user interface{}) *AmocrmRepository_Insert_Call {
return &AmocrmRepository_Insert_Call{Call: _e.mock.On("Insert", ctx, user)}
}
func (_c *AmocrmRepository_Insert_Call) Run(run func(ctx context.Context, user *models.AmocrmUser)) *AmocrmRepository_Insert_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*models.AmocrmUser))
})
return _c
}
func (_c *AmocrmRepository_Insert_Call) Return(_a0 *models.AmocrmUser, _a1 error) *AmocrmRepository_Insert_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AmocrmRepository_Insert_Call) RunAndReturn(run func(context.Context, *models.AmocrmUser) (*models.AmocrmUser, error)) *AmocrmRepository_Insert_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewAmocrmRepository interface {
mock.TestingT
Cleanup(func())
}
// NewAmocrmRepository creates a new instance of AmocrmRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewAmocrmRepository(t mockConstructorTestingTNewAmocrmRepository) *AmocrmRepository {
mock := &AmocrmRepository{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,210 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
models "penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
// AuthService is an autogenerated mock type for the authService type
type AuthService struct {
mock.Mock
}
type AuthService_Expecter struct {
mock *mock.Mock
}
func (_m *AuthService) EXPECT() *AuthService_Expecter {
return &AuthService_Expecter{mock: &_m.Mock}
}
// GetAuthUserByToken provides a mock function with given fields: ctx, accessToken
func (_m *AuthService) GetAuthUserByToken(ctx context.Context, accessToken string) (*models.AuthUser, error) {
ret := _m.Called(ctx, accessToken)
var r0 *models.AuthUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.AuthUser, error)); ok {
return rf(ctx, accessToken)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.AuthUser); ok {
r0 = rf(ctx, accessToken)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.AuthUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, accessToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AuthService_GetAuthUserByToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAuthUserByToken'
type AuthService_GetAuthUserByToken_Call struct {
*mock.Call
}
// GetAuthUserByToken is a helper method to define mock.On call
// - ctx context.Context
// - accessToken string
func (_e *AuthService_Expecter) GetAuthUserByToken(ctx interface{}, accessToken interface{}) *AuthService_GetAuthUserByToken_Call {
return &AuthService_GetAuthUserByToken_Call{Call: _e.mock.On("GetAuthUserByToken", ctx, accessToken)}
}
func (_c *AuthService_GetAuthUserByToken_Call) Run(run func(ctx context.Context, accessToken string)) *AuthService_GetAuthUserByToken_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *AuthService_GetAuthUserByToken_Call) Return(_a0 *models.AuthUser, _a1 error) *AuthService_GetAuthUserByToken_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AuthService_GetAuthUserByToken_Call) RunAndReturn(run func(context.Context, string) (*models.AuthUser, error)) *AuthService_GetAuthUserByToken_Call {
_c.Call.Return(run)
return _c
}
// Login provides a mock function with given fields: ctx, userID
func (_m *AuthService) Login(ctx context.Context, userID string) (*models.Tokens, error) {
ret := _m.Called(ctx, userID)
var r0 *models.Tokens
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.Tokens, error)); ok {
return rf(ctx, userID)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.Tokens); ok {
r0 = rf(ctx, userID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Tokens)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, userID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AuthService_Login_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Login'
type AuthService_Login_Call struct {
*mock.Call
}
// Login is a helper method to define mock.On call
// - ctx context.Context
// - userID string
func (_e *AuthService_Expecter) Login(ctx interface{}, userID interface{}) *AuthService_Login_Call {
return &AuthService_Login_Call{Call: _e.mock.On("Login", ctx, userID)}
}
func (_c *AuthService_Login_Call) Run(run func(ctx context.Context, userID string)) *AuthService_Login_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *AuthService_Login_Call) Return(_a0 *models.Tokens, _a1 error) *AuthService_Login_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AuthService_Login_Call) RunAndReturn(run func(context.Context, string) (*models.Tokens, error)) *AuthService_Login_Call {
_c.Call.Return(run)
return _c
}
// Register provides a mock function with given fields: ctx, information
func (_m *AuthService) Register(ctx context.Context, information *models.AuthUserInformation) (*models.Tokens, string, error) {
ret := _m.Called(ctx, information)
var r0 *models.Tokens
var r1 string
var r2 error
if rf, ok := ret.Get(0).(func(context.Context, *models.AuthUserInformation) (*models.Tokens, string, error)); ok {
return rf(ctx, information)
}
if rf, ok := ret.Get(0).(func(context.Context, *models.AuthUserInformation) *models.Tokens); ok {
r0 = rf(ctx, information)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Tokens)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *models.AuthUserInformation) string); ok {
r1 = rf(ctx, information)
} else {
r1 = ret.Get(1).(string)
}
if rf, ok := ret.Get(2).(func(context.Context, *models.AuthUserInformation) error); ok {
r2 = rf(ctx, information)
} else {
r2 = ret.Error(2)
}
return r0, r1, r2
}
// AuthService_Register_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Register'
type AuthService_Register_Call struct {
*mock.Call
}
// Register is a helper method to define mock.On call
// - ctx context.Context
// - information *models.AuthUserInformation
func (_e *AuthService_Expecter) Register(ctx interface{}, information interface{}) *AuthService_Register_Call {
return &AuthService_Register_Call{Call: _e.mock.On("Register", ctx, information)}
}
func (_c *AuthService_Register_Call) Run(run func(ctx context.Context, information *models.AuthUserInformation)) *AuthService_Register_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*models.AuthUserInformation))
})
return _c
}
func (_c *AuthService_Register_Call) Return(_a0 *models.Tokens, _a1 string, _a2 error) *AuthService_Register_Call {
_c.Call.Return(_a0, _a1, _a2)
return _c
}
func (_c *AuthService_Register_Call) RunAndReturn(run func(context.Context, *models.AuthUserInformation) (*models.Tokens, string, error)) *AuthService_Register_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewAuthService interface {
mock.TestingT
Cleanup(func())
}
// NewAuthService creates a new instance of AuthService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewAuthService(t mockConstructorTestingTNewAuthService) *AuthService {
mock := &AuthService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,92 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
)
// OauthService is an autogenerated mock type for the oauthService type
type OauthService[T interface{}] struct {
mock.Mock
}
type OauthService_Expecter[T interface{}] struct {
mock *mock.Mock
}
func (_m *OauthService[T]) EXPECT() *OauthService_Expecter[T] {
return &OauthService_Expecter[T]{mock: &_m.Mock}
}
// GetUserInformationByCode provides a mock function with given fields: ctx, code
func (_m *OauthService[T]) GetUserInformationByCode(ctx context.Context, code string) (*T, error) {
ret := _m.Called(ctx, code)
var r0 *T
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*T, error)); ok {
return rf(ctx, code)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *T); ok {
r0 = rf(ctx, code)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*T)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, code)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OauthService_GetUserInformationByCode_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserInformationByCode'
type OauthService_GetUserInformationByCode_Call[T interface{}] struct {
*mock.Call
}
// GetUserInformationByCode is a helper method to define mock.On call
// - ctx context.Context
// - code string
func (_e *OauthService_Expecter[T]) GetUserInformationByCode(ctx interface{}, code interface{}) *OauthService_GetUserInformationByCode_Call[T] {
return &OauthService_GetUserInformationByCode_Call[T]{Call: _e.mock.On("GetUserInformationByCode", ctx, code)}
}
func (_c *OauthService_GetUserInformationByCode_Call[T]) Run(run func(ctx context.Context, code string)) *OauthService_GetUserInformationByCode_Call[T] {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *OauthService_GetUserInformationByCode_Call[T]) Return(_a0 *T, _a1 error) *OauthService_GetUserInformationByCode_Call[T] {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *OauthService_GetUserInformationByCode_Call[T]) RunAndReturn(run func(context.Context, string) (*T, error)) *OauthService_GetUserInformationByCode_Call[T] {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewOauthService interface {
mock.TestingT
Cleanup(func())
}
// NewOauthService creates a new instance of OauthService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewOauthService[T interface{}](t mockConstructorTestingTNewOauthService) *OauthService[T] {
mock := &OauthService[T]{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,97 +0,0 @@
package auth
import (
"context"
"fmt"
"time"
"github.com/sirupsen/logrus"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/utils"
)
//go:generate mockery --name authClient
type authClient interface {
GetUser(ctx context.Context, userID string) (*models.AuthUser, error)
Register(ctx context.Context, request *models.RegisterRequest) (*models.Tokens, error)
Exchange(ctx context.Context, userID, signature string) (*models.Tokens, error)
}
//go:generate mockery --name encryptionService
type encryptionService interface {
VerifyJWT(token string) (string, error)
SignCommonSecret() ([]byte, error)
}
type Deps struct {
Logger *logrus.Logger
AuthClient authClient
EncryptionService encryptionService
}
type Service struct {
logger *logrus.Logger
authClient authClient
encryptionService encryptionService
}
func New(deps *Deps) *Service {
return &Service{
logger: deps.Logger,
authClient: deps.AuthClient,
encryptionService: deps.EncryptionService,
}
}
func (receiver *Service) Register(ctx context.Context, information *models.AuthUserInformation) (*models.Tokens, string, error) {
tokens, err := receiver.authClient.Register(ctx, &models.RegisterRequest{
Login: fmt.Sprintf("user_%d", time.Now().UnixNano()),
PhoneNumber: information.PhoneNumber,
Email: information.Email,
Password: utils.GetRandomString(14),
})
if err != nil {
receiver.logger.Errorf("failed to register user on <Register> of <AuthService>: %v", err)
return nil, "", err
}
userID, err := receiver.encryptionService.VerifyJWT(tokens.AccessToken)
if err != nil {
receiver.logger.Errorf("failed to verify jwt on <Register> of <AuthService>: %v", err)
return nil, "", err
}
return tokens, userID, nil
}
func (receiver *Service) Login(ctx context.Context, userID string) (*models.Tokens, error) {
signature, err := receiver.encryptionService.SignCommonSecret()
if err != nil {
receiver.logger.Errorf("failed to sign common secret on <Login> of <AuthService>: %v", err)
return nil, err
}
tokens, err := receiver.authClient.Exchange(ctx, userID, string(signature))
if err != nil {
receiver.logger.Errorf("failed to exchange code on <Login> of <AuthService>: %v", err)
return nil, err
}
return tokens, nil
}
func (receiver *Service) GetAuthUserByToken(ctx context.Context, accessToken string) (*models.AuthUser, error) {
userID, err := receiver.encryptionService.VerifyJWT(accessToken)
if err != nil {
receiver.logger.Errorf("failed to vetify jwt on <GetAuthUserByToken> of <AuthService>: %v", err)
return nil, err
}
user, err := receiver.authClient.GetUser(ctx, userID)
if err != nil {
receiver.logger.Errorf("failed to get user on <GetAuthUserByToken> of <AuthService>: %v", err)
return nil, err
}
return user, nil
}

@ -1,288 +0,0 @@
package auth_test
import (
"context"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/auth"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/auth/mocks"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/validate"
)
var (
userID = "user-id-14"
authUserInformation = models.AuthUserInformation{
PhoneNumber: "+75819369631",
Email: "test@mail.ru",
}
authTokens = models.Tokens{
AccessToken: "access-token-auth",
RefreshToken: "refresh-token-auth",
}
authUser = models.AuthUser{
ID: "1geat",
Login: "gkohoeh",
PhoneNumber: authUserInformation.PhoneNumber,
Email: authUserInformation.Email,
}
validateRegisterRequest = mock.MatchedBy(func(request *models.RegisterRequest) bool {
isLoginFilled := !validate.IsStringEmpty(request.Login)
isPasswordFilled := !validate.IsStringEmpty(request.Password)
isEmailFilled := request.Email == authUserInformation.Email
isPhoneFilled := request.PhoneNumber == authUserInformation.PhoneNumber
if isLoginFilled && isPasswordFilled && isEmailFilled && isPhoneFilled {
return true
}
return false
})
)
func TestAuthRegister(t *testing.T) {
t.Run("Успешная регистарция пользователя", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
registerAuthUserCall := authClient.EXPECT().
Register(mock.Anything, validateRegisterRequest).
Return(&authTokens, nil).
Once()
encryptionService.EXPECT().
VerifyJWT(authTokens.AccessToken).
Return(userID, nil).
NotBefore(registerAuthUserCall).
Once()
registrationTokens, registeredUserID, err := authService.Register(context.Background(), &authUserInformation)
assert.NoError(t, err)
assert.Equal(t, &authTokens, registrationTokens)
assert.Equal(t, userID, registeredUserID)
})
t.Run("Ошибка регистрации пользователя в auth микросервисе при регистрации", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
authClient.EXPECT().
Register(mock.Anything, validateRegisterRequest).
Return(nil, errors.ErrMethodNotImplemented).
Once()
encryptionService.AssertNotCalled(t, "VerifyJWT")
registrationTokens, registeredUserID, err := authService.Register(context.Background(), &authUserInformation)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrMethodNotImplemented.Error())
assert.Nil(t, registrationTokens)
assert.Empty(t, registeredUserID)
})
t.Run("Ошибка подтверждения токена единой системы авторизации при регистрации", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
registerAuthUserCall := authClient.EXPECT().
Register(mock.Anything, validateRegisterRequest).
Return(&authTokens, nil).
Once()
encryptionService.EXPECT().
VerifyJWT(authTokens.AccessToken).
Return("", errors.ErrInvalidArgs).
NotBefore(registerAuthUserCall).
Once()
registrationTokens, registeredUserID, err := authService.Register(context.Background(), &authUserInformation)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrInvalidArgs.Error())
assert.Nil(t, registrationTokens)
assert.Empty(t, registeredUserID)
})
}
func TestAuthLogin(t *testing.T) {
t.Run("Успешная авторизация пользователя", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
encryptCodeCall := encryptionService.EXPECT().
SignCommonSecret().
Return([]byte{116, 116}, nil).
Once()
authClient.EXPECT().
Exchange(mock.Anything, userID, "tt").
Return(&authTokens, nil).
NotBefore(encryptCodeCall).
Once()
loginTokens, err := authService.Login(context.Background(), userID)
assert.NoError(t, err)
assert.Equal(t, &authTokens, loginTokens)
})
t.Run("Ошибка шифрования кода обмена при авторизации", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
encryptionService.EXPECT().
SignCommonSecret().
Return([]byte{111, 116}, errors.ErrInvalidArgs).
Once()
authClient.AssertNotCalled(t, "Exchange")
loginTokens, err := authService.Login(context.Background(), userID)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrInvalidArgs.Error())
assert.Nil(t, loginTokens)
})
t.Run("Ошибка обмена зашифрованного кода на токены при авторизации", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
encryptCodeCall := encryptionService.EXPECT().
SignCommonSecret().
Return([]byte{97, 97}, nil).
Once()
authClient.EXPECT().
Exchange(mock.Anything, userID, "aa").
Return(nil, errors.ErrMethodNotImplemented).
NotBefore(encryptCodeCall).
Once()
loginTokens, err := authService.Login(context.Background(), userID)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrMethodNotImplemented.Error())
assert.Nil(t, loginTokens)
})
}
func TestGetAuthUserByToken(t *testing.T) {
t.Run("Успешное получение информации о пользователе ЕСА через токен", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
encryptCodeCall := encryptionService.EXPECT().
VerifyJWT(authTokens.AccessToken).
Return(userID, nil).
Once()
authClient.EXPECT().
GetUser(mock.Anything, userID).
Return(&authUser, nil).
NotBefore(encryptCodeCall).
Once()
user, err := authService.GetAuthUserByToken(context.Background(), authTokens.AccessToken)
assert.NoError(t, err)
assert.Equal(t, &authUser, user)
})
t.Run("Ошибка запроса получения пользователя ЕСА", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
encryptCodeCall := encryptionService.EXPECT().
VerifyJWT(authTokens.AccessToken).
Return(userID, nil).
Once()
authClient.EXPECT().
GetUser(mock.Anything, userID).
Return(&authUser, errors.ErrMethodNotImplemented).
NotBefore(encryptCodeCall).
Once()
user, err := authService.GetAuthUserByToken(context.Background(), authTokens.AccessToken)
assert.Error(t, err)
assert.NotNil(t, err)
assert.EqualError(t, err, errors.ErrMethodNotImplemented.Error())
assert.Nil(t, user)
})
t.Run("Ошибка подтверждения токена при получении пользователя ЕСА по токену", func(t *testing.T) {
authClient := mocks.NewAuthClient(t)
encryptionService := mocks.NewEncryptionService(t)
authService := auth.New(&auth.Deps{
Logger: logrus.New(),
AuthClient: authClient,
EncryptionService: encryptionService,
})
encryptionService.EXPECT().
VerifyJWT(authTokens.AccessToken).
Return("", errors.ErrEmptyArgs).
Once()
authClient.AssertNotCalled(t, "GetUser")
user, err := authService.GetAuthUserByToken(context.Background(), authTokens.AccessToken)
assert.Error(t, err)
assert.NotNil(t, err)
assert.EqualError(t, err, errors.ErrEmptyArgs.Error())
assert.Nil(t, user)
})
}

@ -1,204 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
models "penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
// AuthClient is an autogenerated mock type for the authClient type
type AuthClient struct {
mock.Mock
}
type AuthClient_Expecter struct {
mock *mock.Mock
}
func (_m *AuthClient) EXPECT() *AuthClient_Expecter {
return &AuthClient_Expecter{mock: &_m.Mock}
}
// Exchange provides a mock function with given fields: ctx, userID, signature
func (_m *AuthClient) Exchange(ctx context.Context, userID string, signature string) (*models.Tokens, error) {
ret := _m.Called(ctx, userID, signature)
var r0 *models.Tokens
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) (*models.Tokens, error)); ok {
return rf(ctx, userID, signature)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string) *models.Tokens); ok {
r0 = rf(ctx, userID, signature)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Tokens)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = rf(ctx, userID, signature)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AuthClient_Exchange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Exchange'
type AuthClient_Exchange_Call struct {
*mock.Call
}
// Exchange is a helper method to define mock.On call
// - ctx context.Context
// - userID string
// - signature string
func (_e *AuthClient_Expecter) Exchange(ctx interface{}, userID interface{}, signature interface{}) *AuthClient_Exchange_Call {
return &AuthClient_Exchange_Call{Call: _e.mock.On("Exchange", ctx, userID, signature)}
}
func (_c *AuthClient_Exchange_Call) Run(run func(ctx context.Context, userID string, signature string)) *AuthClient_Exchange_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string), args[2].(string))
})
return _c
}
func (_c *AuthClient_Exchange_Call) Return(_a0 *models.Tokens, _a1 error) *AuthClient_Exchange_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AuthClient_Exchange_Call) RunAndReturn(run func(context.Context, string, string) (*models.Tokens, error)) *AuthClient_Exchange_Call {
_c.Call.Return(run)
return _c
}
// GetUser provides a mock function with given fields: ctx, userID
func (_m *AuthClient) GetUser(ctx context.Context, userID string) (*models.AuthUser, error) {
ret := _m.Called(ctx, userID)
var r0 *models.AuthUser
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*models.AuthUser, error)); ok {
return rf(ctx, userID)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *models.AuthUser); ok {
r0 = rf(ctx, userID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.AuthUser)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, userID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AuthClient_GetUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUser'
type AuthClient_GetUser_Call struct {
*mock.Call
}
// GetUser is a helper method to define mock.On call
// - ctx context.Context
// - userID string
func (_e *AuthClient_Expecter) GetUser(ctx interface{}, userID interface{}) *AuthClient_GetUser_Call {
return &AuthClient_GetUser_Call{Call: _e.mock.On("GetUser", ctx, userID)}
}
func (_c *AuthClient_GetUser_Call) Run(run func(ctx context.Context, userID string)) *AuthClient_GetUser_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *AuthClient_GetUser_Call) Return(_a0 *models.AuthUser, _a1 error) *AuthClient_GetUser_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AuthClient_GetUser_Call) RunAndReturn(run func(context.Context, string) (*models.AuthUser, error)) *AuthClient_GetUser_Call {
_c.Call.Return(run)
return _c
}
// Register provides a mock function with given fields: ctx, request
func (_m *AuthClient) Register(ctx context.Context, request *models.RegisterRequest) (*models.Tokens, error) {
ret := _m.Called(ctx, request)
var r0 *models.Tokens
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *models.RegisterRequest) (*models.Tokens, error)); ok {
return rf(ctx, request)
}
if rf, ok := ret.Get(0).(func(context.Context, *models.RegisterRequest) *models.Tokens); ok {
r0 = rf(ctx, request)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.Tokens)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *models.RegisterRequest) error); ok {
r1 = rf(ctx, request)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// AuthClient_Register_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Register'
type AuthClient_Register_Call struct {
*mock.Call
}
// Register is a helper method to define mock.On call
// - ctx context.Context
// - request *models.RegisterRequest
func (_e *AuthClient_Expecter) Register(ctx interface{}, request interface{}) *AuthClient_Register_Call {
return &AuthClient_Register_Call{Call: _e.mock.On("Register", ctx, request)}
}
func (_c *AuthClient_Register_Call) Run(run func(ctx context.Context, request *models.RegisterRequest)) *AuthClient_Register_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*models.RegisterRequest))
})
return _c
}
func (_c *AuthClient_Register_Call) Return(_a0 *models.Tokens, _a1 error) *AuthClient_Register_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *AuthClient_Register_Call) RunAndReturn(run func(context.Context, *models.RegisterRequest) (*models.Tokens, error)) *AuthClient_Register_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewAuthClient interface {
mock.TestingT
Cleanup(func())
}
// NewAuthClient creates a new instance of AuthClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewAuthClient(t mockConstructorTestingTNewAuthClient) *AuthClient {
mock := &AuthClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,138 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// EncryptionService is an autogenerated mock type for the encryptionService type
type EncryptionService struct {
mock.Mock
}
type EncryptionService_Expecter struct {
mock *mock.Mock
}
func (_m *EncryptionService) EXPECT() *EncryptionService_Expecter {
return &EncryptionService_Expecter{mock: &_m.Mock}
}
// SignCommonSecret provides a mock function with given fields:
func (_m *EncryptionService) SignCommonSecret() ([]byte, error) {
ret := _m.Called()
var r0 []byte
var r1 error
if rf, ok := ret.Get(0).(func() ([]byte, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() []byte); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EncryptionService_SignCommonSecret_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SignCommonSecret'
type EncryptionService_SignCommonSecret_Call struct {
*mock.Call
}
// SignCommonSecret is a helper method to define mock.On call
func (_e *EncryptionService_Expecter) SignCommonSecret() *EncryptionService_SignCommonSecret_Call {
return &EncryptionService_SignCommonSecret_Call{Call: _e.mock.On("SignCommonSecret")}
}
func (_c *EncryptionService_SignCommonSecret_Call) Run(run func()) *EncryptionService_SignCommonSecret_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *EncryptionService_SignCommonSecret_Call) Return(_a0 []byte, _a1 error) *EncryptionService_SignCommonSecret_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *EncryptionService_SignCommonSecret_Call) RunAndReturn(run func() ([]byte, error)) *EncryptionService_SignCommonSecret_Call {
_c.Call.Return(run)
return _c
}
// VerifyJWT provides a mock function with given fields: token
func (_m *EncryptionService) VerifyJWT(token string) (string, error) {
ret := _m.Called(token)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
return rf(token)
}
if rf, ok := ret.Get(0).(func(string) string); ok {
r0 = rf(token)
} else {
r0 = ret.Get(0).(string)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(token)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EncryptionService_VerifyJWT_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VerifyJWT'
type EncryptionService_VerifyJWT_Call struct {
*mock.Call
}
// VerifyJWT is a helper method to define mock.On call
// - token string
func (_e *EncryptionService_Expecter) VerifyJWT(token interface{}) *EncryptionService_VerifyJWT_Call {
return &EncryptionService_VerifyJWT_Call{Call: _e.mock.On("VerifyJWT", token)}
}
func (_c *EncryptionService_VerifyJWT_Call) Run(run func(token string)) *EncryptionService_VerifyJWT_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *EncryptionService_VerifyJWT_Call) Return(_a0 string, _a1 error) *EncryptionService_VerifyJWT_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *EncryptionService_VerifyJWT_Call) RunAndReturn(run func(string) (string, error)) *EncryptionService_VerifyJWT_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewEncryptionService interface {
mock.TestingT
Cleanup(func())
}
// NewEncryptionService creates a new instance of EncryptionService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewEncryptionService(t mockConstructorTestingTNewEncryptionService) *EncryptionService {
mock := &EncryptionService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,103 +0,0 @@
package encrypt
import (
"crypto/ed25519"
"crypto/x509"
"encoding/pem"
"fmt"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
//go:generate mockery --name jwtUtil
type jwtUtil interface {
Validate(string) (*models.JWTAuthUser, error)
}
type ServiceDeps struct {
JWT jwtUtil
/* Публичный ключ для верификации подписи кривой Эдвардса (Edwards curve) */
PublicCurveKey string
/* Приватный ключ для верификации подписи кривой Эдвардса (Edwards curve) */
PrivateCurveKey string
/*
Обший секретный знаменатель для шифрования и верификации подписи
(должен быть одинаковым для всех микросервисов)
*/
SignSecret string
}
type Service struct {
jwt jwtUtil
publicCurveKey string
privateCurveKey string
signSecret string
}
func New(deps *ServiceDeps) *Service {
return &Service{
jwt: deps.JWT,
publicCurveKey: deps.PublicCurveKey,
privateCurveKey: deps.PrivateCurveKey,
signSecret: deps.SignSecret,
}
}
func (receiver *Service) VerifySignature(signature []byte) (isValid bool, err error) {
defer func() {
if recovered := recover(); recovered != nil {
err = fmt.Errorf("recovered sign error on <VerifySignature> of <EncryptService>: %v", recovered)
}
}()
block, _ := pem.Decode([]byte(receiver.publicCurveKey))
if block == nil {
return false, fmt.Errorf("public key block is nil")
}
rawPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return false, fmt.Errorf("failed parse public key on <VerifySignature> of <EncryptService>: %w", err)
}
publicKey, ok := rawPublicKey.(ed25519.PublicKey)
if !ok {
return false, fmt.Errorf("failed convert to ed25519.PrivateKey on <VerifySignature> of <EncryptService>: %w", err)
}
return ed25519.Verify(publicKey, []byte(receiver.signSecret), signature), nil
}
func (receiver *Service) SignCommonSecret() (signature []byte, err error) {
defer func() {
if recovered := recover(); recovered != nil {
err = fmt.Errorf("recovered sign error on <SignCommonSecret> of <EncryptService>: %v", recovered)
}
}()
block, _ := pem.Decode([]byte(receiver.privateCurveKey))
rawPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return []byte{}, fmt.Errorf("failed parse private key on <SignCommonSecret> of <EncryptService>: %w", err)
}
privateKey, ok := rawPrivateKey.(ed25519.PrivateKey)
if !ok {
return []byte{}, fmt.Errorf("failed convert to ed25519.PrivateKey on <SignCommonSecret> of <EncryptService>: %w", err)
}
return ed25519.Sign(privateKey, []byte(receiver.signSecret)), nil
}
func (receiver *Service) VerifyJWT(token string) (string, error) {
validatedJwtPayload, err := receiver.jwt.Validate(token)
if err != nil {
return "", fmt.Errorf("failed to verify jwt on <VerifyJWT> of <EncryptService>: %w", err)
}
return validatedJwtPayload.ID, nil
}

@ -1,182 +0,0 @@
package encrypt_test
import (
"errors"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/encrypt"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/encrypt/mocks"
)
var (
privateKeyCurve25519 = strings.Replace(
`-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIKn0BKwF3vZvODgWAnUIwQhd8de5oZhY48gc23EWfrfs
-----END PRIVATE KEY-----`,
"\t",
"",
-1,
)
privateKeyCurve25519Invalid = strings.Replace(
`-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIE3vZvODgWAnUIhd8de5oZhY48gc23EWfrfs
-----END PRIVATE KEY-----`,
"\t",
"",
-1,
)
publicKeyCurve25519 = strings.Replace(
`-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAEbnIvjIMle4rqVol6K2XUqOxHy1KJoNoZdKJrRUPKL4=
-----END PUBLIC KEY-----`,
"\t",
"",
-1,
)
publicKeyCurve25519InvalidLength = strings.Replace(
`-----BEGIN PUBLIC KEY-----
MowBQYDK2VwA9yEAEbnIvjIMle4rqVol6K2XUqOxHy1KJoNoZdKJrRUPKL4=
-----END PUBLIC KEY-----`,
"\t",
"",
-1,
)
)
func TestSignCommonSecret(t *testing.T) {
t.Run("Успешная подпись общего секрета", func(t *testing.T) {
encryptService := encrypt.New(&encrypt.ServiceDeps{
PrivateCurveKey: privateKeyCurve25519,
SignSecret: "secret",
})
assert.NotPanics(t, func() {
encryptedText, err := encryptService.SignCommonSecret()
assert.NoError(t, err)
assert.NotEmpty(t, encryptedText)
assert.NotZero(t, encryptedText)
})
})
t.Run("Ошибка подписи из-за кривого ключа (заголовок имеется)", func(t *testing.T) {
encryptService := encrypt.New(&encrypt.ServiceDeps{
PrivateCurveKey: privateKeyCurve25519Invalid,
SignSecret: "secret",
})
assert.NotPanics(t, func() {
encryptedText, err := encryptService.SignCommonSecret()
assert.Error(t, err)
assert.Empty(t, encryptedText)
assert.Zero(t, encryptedText)
})
})
t.Run("Ошибка подписи из-за рандомного кривого ключа", func(t *testing.T) {
encryptService := encrypt.New(&encrypt.ServiceDeps{
PrivateCurveKey: "testtesttesttest",
SignSecret: "secret",
})
assert.NotPanics(t, func() {
encryptedText, err := encryptService.SignCommonSecret()
assert.Error(t, err)
assert.Empty(t, encryptedText)
assert.Zero(t, encryptedText)
})
})
}
func TestVerifySignature(t *testing.T) {
t.Run("Успешное подтвеждение подписи", func(t *testing.T) {
encryptService := encrypt.New(&encrypt.ServiceDeps{
PublicCurveKey: publicKeyCurve25519,
PrivateCurveKey: privateKeyCurve25519,
SignSecret: "secret",
})
assert.NotPanics(t, func() {
signature, _ := encryptService.SignCommonSecret()
isValid, err := encryptService.VerifySignature(signature)
assert.NoError(t, err)
assert.Equal(t, true, isValid)
})
})
t.Run("Неудачное подтверждение подписи из-за невалидности ключа", func(t *testing.T) {
encryptService := encrypt.New(&encrypt.ServiceDeps{
PublicCurveKey: "teettaegarehah",
PrivateCurveKey: privateKeyCurve25519,
SignSecret: "secret",
})
assert.NotPanics(t, func() {
signature, _ := encryptService.SignCommonSecret()
isValid, err := encryptService.VerifySignature(signature)
assert.Error(t, err)
assert.Equal(t, false, isValid)
})
})
t.Run("Неудачное подтверждение подписи при использовании ключа у которого невалидный размер (слишком большой)", func(t *testing.T) {
encryptService := encrypt.New(&encrypt.ServiceDeps{
PublicCurveKey: publicKeyCurve25519InvalidLength,
PrivateCurveKey: privateKeyCurve25519,
SignSecret: "secret",
})
assert.NotPanics(t, func() {
signature, _ := encryptService.SignCommonSecret()
isValid, err := encryptService.VerifySignature(signature)
assert.Error(t, err)
assert.Equal(t, false, isValid)
})
})
}
func TestVerifyJWT(t *testing.T) {
jwtToken := "token-token"
jwtUser := models.JWTAuthUser{
ID: "id1",
}
t.Run("Успешное подтверждение токена", func(t *testing.T) {
jwtUtil := mocks.NewJwtUtil(t)
encryptService := encrypt.New(&encrypt.ServiceDeps{
JWT: jwtUtil,
})
jwtUtil.EXPECT().Validate(jwtToken).Return(&jwtUser, nil).Once()
id, err := encryptService.VerifyJWT(jwtToken)
assert.NoError(t, err)
assert.Equal(t, jwtUser.ID, id)
})
t.Run("Ошибка подтверждения токена", func(t *testing.T) {
jwtUtil := mocks.NewJwtUtil(t)
encryptService := encrypt.New(&encrypt.ServiceDeps{
JWT: jwtUtil,
})
jwtUtil.EXPECT().Validate(jwtToken).Return(nil, errors.New("validate jwt error")).Once()
id, err := encryptService.VerifyJWT(jwtToken)
assert.Error(t, err)
assert.Empty(t, id)
})
}

@ -1,90 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
models "penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
)
// JwtUtil is an autogenerated mock type for the jwtUtil type
type JwtUtil struct {
mock.Mock
}
type JwtUtil_Expecter struct {
mock *mock.Mock
}
func (_m *JwtUtil) EXPECT() *JwtUtil_Expecter {
return &JwtUtil_Expecter{mock: &_m.Mock}
}
// Validate provides a mock function with given fields: _a0
func (_m *JwtUtil) Validate(_a0 string) (*models.JWTAuthUser, error) {
ret := _m.Called(_a0)
var r0 *models.JWTAuthUser
var r1 error
if rf, ok := ret.Get(0).(func(string) (*models.JWTAuthUser, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(string) *models.JWTAuthUser); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*models.JWTAuthUser)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// JwtUtil_Validate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Validate'
type JwtUtil_Validate_Call struct {
*mock.Call
}
// Validate is a helper method to define mock.On call
// - _a0 string
func (_e *JwtUtil_Expecter) Validate(_a0 interface{}) *JwtUtil_Validate_Call {
return &JwtUtil_Validate_Call{Call: _e.mock.On("Validate", _a0)}
}
func (_c *JwtUtil_Validate_Call) Run(run func(_a0 string)) *JwtUtil_Validate_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *JwtUtil_Validate_Call) Return(_a0 *models.JWTAuthUser, _a1 error) *JwtUtil_Validate_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *JwtUtil_Validate_Call) RunAndReturn(run func(string) (*models.JWTAuthUser, error)) *JwtUtil_Validate_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewJwtUtil interface {
mock.TestingT
Cleanup(func())
}
// NewJwtUtil creates a new instance of JwtUtil. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewJwtUtil(t mockConstructorTestingTNewJwtUtil) *JwtUtil {
mock := &JwtUtil{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,166 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
oauth2 "golang.org/x/oauth2"
)
// OauthClient is an autogenerated mock type for the oauthClient type
type OauthClient struct {
mock.Mock
}
type OauthClient_Expecter struct {
mock *mock.Mock
}
func (_m *OauthClient) EXPECT() *OauthClient_Expecter {
return &OauthClient_Expecter{mock: &_m.Mock}
}
// AuthCodeURL provides a mock function with given fields: state, opts
func (_m *OauthClient) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, state)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 string
if rf, ok := ret.Get(0).(func(string, ...oauth2.AuthCodeOption) string); ok {
r0 = rf(state, opts...)
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// OauthClient_AuthCodeURL_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AuthCodeURL'
type OauthClient_AuthCodeURL_Call struct {
*mock.Call
}
// AuthCodeURL is a helper method to define mock.On call
// - state string
// - opts ...oauth2.AuthCodeOption
func (_e *OauthClient_Expecter) AuthCodeURL(state interface{}, opts ...interface{}) *OauthClient_AuthCodeURL_Call {
return &OauthClient_AuthCodeURL_Call{Call: _e.mock.On("AuthCodeURL",
append([]interface{}{state}, opts...)...)}
}
func (_c *OauthClient_AuthCodeURL_Call) Run(run func(state string, opts ...oauth2.AuthCodeOption)) *OauthClient_AuthCodeURL_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]oauth2.AuthCodeOption, len(args)-1)
for i, a := range args[1:] {
if a != nil {
variadicArgs[i] = a.(oauth2.AuthCodeOption)
}
}
run(args[0].(string), variadicArgs...)
})
return _c
}
func (_c *OauthClient_AuthCodeURL_Call) Return(_a0 string) *OauthClient_AuthCodeURL_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *OauthClient_AuthCodeURL_Call) RunAndReturn(run func(string, ...oauth2.AuthCodeOption) string) *OauthClient_AuthCodeURL_Call {
_c.Call.Return(run)
return _c
}
// Exchange provides a mock function with given fields: ctx, code, opts
func (_m *OauthClient) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, code)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *oauth2.Token
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, ...oauth2.AuthCodeOption) (*oauth2.Token, error)); ok {
return rf(ctx, code, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, string, ...oauth2.AuthCodeOption) *oauth2.Token); ok {
r0 = rf(ctx, code, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*oauth2.Token)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string, ...oauth2.AuthCodeOption) error); ok {
r1 = rf(ctx, code, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OauthClient_Exchange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Exchange'
type OauthClient_Exchange_Call struct {
*mock.Call
}
// Exchange is a helper method to define mock.On call
// - ctx context.Context
// - code string
// - opts ...oauth2.AuthCodeOption
func (_e *OauthClient_Expecter) Exchange(ctx interface{}, code interface{}, opts ...interface{}) *OauthClient_Exchange_Call {
return &OauthClient_Exchange_Call{Call: _e.mock.On("Exchange",
append([]interface{}{ctx, code}, opts...)...)}
}
func (_c *OauthClient_Exchange_Call) Run(run func(ctx context.Context, code string, opts ...oauth2.AuthCodeOption)) *OauthClient_Exchange_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]oauth2.AuthCodeOption, len(args)-2)
for i, a := range args[2:] {
if a != nil {
variadicArgs[i] = a.(oauth2.AuthCodeOption)
}
}
run(args[0].(context.Context), args[1].(string), variadicArgs...)
})
return _c
}
func (_c *OauthClient_Exchange_Call) Return(_a0 *oauth2.Token, _a1 error) *OauthClient_Exchange_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *OauthClient_Exchange_Call) RunAndReturn(run func(context.Context, string, ...oauth2.AuthCodeOption) (*oauth2.Token, error)) *OauthClient_Exchange_Call {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewOauthClient interface {
mock.TestingT
Cleanup(func())
}
// NewOauthClient creates a new instance of OauthClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewOauthClient(t mockConstructorTestingTNewOauthClient) *OauthClient {
mock := &OauthClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,92 +0,0 @@
// Code generated by mockery v2.26.0. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
)
// ServiceClient is an autogenerated mock type for the serviceClient type
type ServiceClient[T interface{}] struct {
mock.Mock
}
type ServiceClient_Expecter[T interface{}] struct {
mock *mock.Mock
}
func (_m *ServiceClient[T]) EXPECT() *ServiceClient_Expecter[T] {
return &ServiceClient_Expecter[T]{mock: &_m.Mock}
}
// GetUserInformation provides a mock function with given fields: ctx, accessToken
func (_m *ServiceClient[T]) GetUserInformation(ctx context.Context, accessToken string) (*T, error) {
ret := _m.Called(ctx, accessToken)
var r0 *T
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*T, error)); ok {
return rf(ctx, accessToken)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *T); ok {
r0 = rf(ctx, accessToken)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*T)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, accessToken)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ServiceClient_GetUserInformation_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUserInformation'
type ServiceClient_GetUserInformation_Call[T interface{}] struct {
*mock.Call
}
// GetUserInformation is a helper method to define mock.On call
// - ctx context.Context
// - accessToken string
func (_e *ServiceClient_Expecter[T]) GetUserInformation(ctx interface{}, accessToken interface{}) *ServiceClient_GetUserInformation_Call[T] {
return &ServiceClient_GetUserInformation_Call[T]{Call: _e.mock.On("GetUserInformation", ctx, accessToken)}
}
func (_c *ServiceClient_GetUserInformation_Call[T]) Run(run func(ctx context.Context, accessToken string)) *ServiceClient_GetUserInformation_Call[T] {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *ServiceClient_GetUserInformation_Call[T]) Return(_a0 *T, _a1 error) *ServiceClient_GetUserInformation_Call[T] {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *ServiceClient_GetUserInformation_Call[T]) RunAndReturn(run func(context.Context, string) (*T, error)) *ServiceClient_GetUserInformation_Call[T] {
_c.Call.Return(run)
return _c
}
type mockConstructorTestingTNewServiceClient interface {
mock.TestingT
Cleanup(func())
}
// NewServiceClient creates a new instance of ServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewServiceClient[T interface{}](t mockConstructorTestingTNewServiceClient) *ServiceClient[T] {
mock := &ServiceClient[T]{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

@ -1,85 +0,0 @@
package oauth
import (
"context"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/utils"
)
/*
TODO:
- Реализовать рандомную генерацию state. Каждый раз при генерации ссылки (auth/link),
для этой ссылки должен генерироваться новый state. Так же при реализации этого функционала,
необходимо решить, где хранить этот временный state: MongoDB, Reddis, RAM
- Покрыть тестами генерацию рандомного state
*/
//go:generate mockery --name serviceClient
type serviceClient[T any] interface {
GetUserInformation(ctx context.Context, accessToken string) (*T, error)
}
//go:generate mockery --name oauthClient
type oauthClient interface {
Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error)
AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
}
type Deps[T any] struct {
Logger *logrus.Logger
ServiceClient serviceClient[T]
OAuthClient oauthClient
}
type Service[T any] struct {
logger *logrus.Logger
serviceClient serviceClient[T]
oauthClient oauthClient
state string
}
func New[T any](deps *Deps[T]) *Service[T] {
return &Service[T]{
logger: deps.Logger,
serviceClient: deps.ServiceClient,
oauthClient: deps.OAuthClient,
state: utils.GetRandomString(10),
}
}
func (receiver *Service[T]) GetUserInformationByCode(ctx context.Context, code string) (*T, error) {
token, err := receiver.oauthClient.Exchange(ctx, code)
if err != nil {
receiver.logger.Errorf("failed to exchange token on <GetSocialUserInformationByCode> of <AuthService>: %v", err)
return nil, err
}
userInformation, err := receiver.serviceClient.GetUserInformation(ctx, token.AccessToken)
if err != nil {
receiver.logger.Errorf("failed to get user information on <GetSocialUserInformationByCode> of <AuthService>: %v", err)
return nil, err
}
return userInformation, nil
}
func (receiver *Service[any]) GenerateAuthURL() string {
return receiver.oauthClient.AuthCodeURL(receiver.GetState(), oauth2.AccessTypeOffline)
}
func (receiver *Service[any]) GenerateLinkURL(accessToken string) string {
setAccessTokenParam := oauth2.SetAuthURLParam("accessToken", accessToken)
return receiver.oauthClient.AuthCodeURL(receiver.GetState(), oauth2.AccessTypeOffline, setAccessTokenParam)
}
func (receiver *Service[any]) ValidateState(state string) bool {
return state == receiver.state
}
func (receiver *Service[any]) GetState() string {
return receiver.state
}

@ -1,167 +0,0 @@
package oauth_test
import (
"context"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"golang.org/x/oauth2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/oauth"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/service/oauth/mocks"
)
type SocialUserInformation struct {
ID string
}
var (
code = "code"
socialUserInformation = SocialUserInformation{
ID: "userid1",
}
oauthTokens = oauth2.Token{
AccessToken: "access-token-auth",
RefreshToken: "refresh-token-auth",
}
)
func TestGetUserInformationByCode(t *testing.T) {
t.Run("Успешное получение информации о пользователе oauth через код", func(t *testing.T) {
serviceClient := mocks.NewServiceClient[SocialUserInformation](t)
oauthClient := mocks.NewOauthClient(t)
oauthService := oauth.New(&oauth.Deps[SocialUserInformation]{
Logger: logrus.New(),
ServiceClient: serviceClient,
OAuthClient: oauthClient,
})
oauthCodeExchangeCall := oauthClient.EXPECT().
Exchange(mock.Anything, code).
Return(&oauthTokens, nil).
Once()
serviceClient.EXPECT().
GetUserInformation(mock.Anything, oauthTokens.AccessToken).
Return(&socialUserInformation, nil).
NotBefore(oauthCodeExchangeCall).
Once()
information, err := oauthService.GetUserInformationByCode(context.Background(), code)
assert.NoError(t, err)
assert.Equal(t, &socialUserInformation, information)
})
t.Run("Ошибка при запросе за информацией о пользователе oauth системы", func(t *testing.T) {
serviceClient := mocks.NewServiceClient[SocialUserInformation](t)
oauthClient := mocks.NewOauthClient(t)
oauthService := oauth.New(&oauth.Deps[SocialUserInformation]{
Logger: logrus.New(),
ServiceClient: serviceClient,
OAuthClient: oauthClient,
})
oauthCodeExchangeCall := oauthClient.EXPECT().
Exchange(mock.Anything, code).
Return(&oauthTokens, nil).
Once()
serviceClient.EXPECT().
GetUserInformation(mock.Anything, oauthTokens.AccessToken).
Return(nil, errors.ErrEmptyArgs).
NotBefore(oauthCodeExchangeCall).
Once()
information, err := oauthService.GetUserInformationByCode(context.Background(), code)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrEmptyArgs.Error())
assert.Nil(t, information)
})
t.Run("Ошибка при обмене ключа на токены при получении информации об oauth пользователе", func(t *testing.T) {
serviceClient := mocks.NewServiceClient[SocialUserInformation](t)
oauthClient := mocks.NewOauthClient(t)
oauthService := oauth.New(&oauth.Deps[SocialUserInformation]{
Logger: logrus.New(),
ServiceClient: serviceClient,
OAuthClient: oauthClient,
})
oauthClient.EXPECT().
Exchange(mock.Anything, code).
Return(nil, errors.ErrEmptyArgs).
Once()
serviceClient.AssertNotCalled(t, "GetUserInformation")
information, err := oauthService.GetUserInformationByCode(context.Background(), code)
assert.Error(t, err)
assert.EqualError(t, err, errors.ErrEmptyArgs.Error())
assert.Nil(t, information)
})
}
func TestGenerateURL(t *testing.T) {
generatedLink := "https://link"
accessToken := "access-token"
t.Run("Успешная генерация ссылки авторизации", func(t *testing.T) {
oauthClient := mocks.NewOauthClient(t)
oauthService := oauth.New(&oauth.Deps[any]{
OAuthClient: oauthClient,
})
oauthClient.EXPECT().AuthCodeURL(oauthService.GetState(), oauth2.AccessTypeOffline).Return(generatedLink).Once()
assert.Equal(t, generatedLink, oauthService.GenerateAuthURL())
})
t.Run("Успешная генерация ссылки привязки аккаунта", func(t *testing.T) {
oauthClient := mocks.NewOauthClient(t)
oauthService := oauth.New(&oauth.Deps[any]{
OAuthClient: oauthClient,
})
oauthClient.EXPECT().
AuthCodeURL(
oauthService.GetState(),
oauth2.AccessTypeOffline,
oauth2.SetAuthURLParam("accessToken", accessToken),
).
Return(generatedLink).
Once()
assert.Equal(t, generatedLink, oauthService.GenerateLinkURL(accessToken))
})
}
func TestGetState(t *testing.T) {
t.Run("Успешное получение state", func(t *testing.T) {
oauthService := oauth.New(&oauth.Deps[any]{})
assert.NotEmpty(t, oauthService.GetState())
assert.NotNil(t, oauthService.GetState())
assert.NotZero(t, oauthService.GetState())
})
}
func TestValidateState(t *testing.T) {
t.Run("Невалидный state", func(t *testing.T) {
oauthService := oauth.New(&oauth.Deps[any]{})
assert.Equal(t, false, oauthService.ValidateState(""))
})
t.Run("Валидный state", func(t *testing.T) {
oauthService := oauth.New(&oauth.Deps[any]{})
assert.Equal(t, true, oauthService.ValidateState(oauthService.GetState()))
})
}

@ -0,0 +1,3 @@
package swagger
//go:generate oapi-codegen --config oapi-codegen.yaml ../../openapi.yaml

@ -0,0 +1,7 @@
output: server.gen.go
package: swagger
generate:
# other servers are available!
echo-server: true
models: true
embedded-spec: true

@ -0,0 +1,572 @@
// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT.
package swagger
import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"net/http"
"net/url"
"path"
"strings"
"time"
"github.com/deepmap/oapi-codegen/pkg/runtime"
"github.com/getkin/kin-openapi/openapi3"
"github.com/labstack/echo/v4"
)
// Defines values for RecordType.
const (
BuyCart RecordType = "buyCart"
DeclinedPayment RecordType = "declinedPayment"
SubsriptionEnd RecordType = "subsriptionEnd"
SuccessfulPayment RecordType = "successfulPayment"
TimeoutPayment RecordType = "timeoutPayment"
)
// Account defines model for Account.
type Account struct {
Id *string `json:"_id,omitempty"`
Cart *Cart `json:"cart,omitempty"`
IsDeleted *bool `json:"isDeleted,omitempty"`
UserId *string `json:"userId,omitempty"`
Wallet *Wallet `json:"wallet,omitempty"`
}
// Cart defines model for Cart.
type Cart = []string
// Record defines model for Record.
type Record struct {
Comment *string `json:"comment,omitempty"`
CreatedAt *time.Time `json:"createdAt,omitempty"`
Id *string `json:"id,omitempty"`
// Subject я пока не могу предположить, какие будут фильтры по истории, поэтому предлагаю в это поле просто класть строку с json. ибо для каждого типа записи она своя.
Subject *string `json:"subject,omitempty"`
Type *RecordType `json:"type,omitempty"`
UserId *string `json:"userId,omitempty"`
}
// RecordType defines model for Record.Type.
type RecordType string
// Wallet defines model for Wallet.
type Wallet struct {
Cash *int64 `json:"cash,omitempty"`
Currency *string `json:"currency,omitempty"`
// Money деньги на счету в копейках. Чтобы при перессчётах не возникало денег изниоткуда. фиксируемся к одной валюте, она будет внутренней, никому её не покажем
Money *int64 `json:"money,omitempty"`
}
// PaginationAccountsParams defines parameters for PaginationAccounts.
type PaginationAccountsParams struct {
// P Номер страницы, начиная с 0
P *int `form:"p,omitempty" json:"p,omitempty"`
// S размер страницы
S *int `form:"s,omitempty" json:"s,omitempty"`
}
// RemoveFromCartParams defines parameters for RemoveFromCart.
type RemoveFromCartParams struct {
Id string `form:"id" json:"id"`
}
// Add2cartJSONBody defines parameters for Add2cart.
type Add2cartJSONBody struct {
Id *string `json:"id,omitempty"`
}
// PayCartJSONBody defines parameters for PayCart.
type PayCartJSONBody struct {
// Id айдишник для того, чтобы указать сервису оплаты, какой юзер оплатил
Id *string `json:"id,omitempty"`
}
// UpdateCurrenciesJSONBody defines parameters for UpdateCurrencies.
type UpdateCurrenciesJSONBody = []string
// GetHistoryParams defines parameters for GetHistory.
type GetHistoryParams struct {
// P Номер страницы, начиная с 0
P *int `form:"p,omitempty" json:"p,omitempty"`
// S размер страницы
S *int `form:"s,omitempty" json:"s,omitempty"`
}
// ChangeCurrencyJSONBody defines parameters for ChangeCurrency.
type ChangeCurrencyJSONBody struct {
Currency *string `json:"currency,omitempty"`
}
// RequestMoneyJSONBody defines parameters for RequestMoney.
type RequestMoneyJSONBody struct {
Cash *int `json:"cash,omitempty"`
}
// PutMoneyJSONBody defines parameters for PutMoney.
type PutMoneyJSONBody struct {
Cash *int `json:"cash,omitempty"`
Currency *string `json:"currency,omitempty"`
Id *string `json:"id,omitempty"`
}
// Add2cartJSONRequestBody defines body for Add2cart for application/json ContentType.
type Add2cartJSONRequestBody Add2cartJSONBody
// PayCartJSONRequestBody defines body for PayCart for application/json ContentType.
type PayCartJSONRequestBody PayCartJSONBody
// UpdateCurrenciesJSONRequestBody defines body for UpdateCurrencies for application/json ContentType.
type UpdateCurrenciesJSONRequestBody = UpdateCurrenciesJSONBody
// Add2historyJSONRequestBody defines body for Add2history for application/json ContentType.
type Add2historyJSONRequestBody = Record
// ChangeCurrencyJSONRequestBody defines body for ChangeCurrency for application/json ContentType.
type ChangeCurrencyJSONRequestBody ChangeCurrencyJSONBody
// RequestMoneyJSONRequestBody defines body for RequestMoney for application/json ContentType.
type RequestMoneyJSONRequestBody RequestMoneyJSONBody
// PutMoneyJSONRequestBody defines body for PutMoney for application/json ContentType.
type PutMoneyJSONRequestBody PutMoneyJSONBody
// ServerInterface represents all server handlers.
type ServerInterface interface {
// удалить собственный аккаунт
// (DELETE /account)
DeleteAccount(ctx echo.Context) error
// Получение текущего аккаунта юзера
// (GET /account)
GetAccount(ctx echo.Context) error
// создать новый аккаунт
// (POST /account)
AddAccount(ctx echo.Context) error
// удалить аккаунт по айди
// (DELETE /account/{accountId})
DeleteDirectAccount(ctx echo.Context, accountId string) error
// получить аккаунт по айди
// (GET /account/{accountId})
GetDirectAccount(ctx echo.Context, accountId string) error
// списко аккаунтов с пагинацией
// (GET /accounts)
PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error
// удаляем из корзины тариф
// (DELETE /cart)
RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error
// добавляем в корзину тариф
// (PUT /cart)
Add2cart(ctx echo.Context) error
// оплатить козину
// (POST /cart/pay)
PayCart(ctx echo.Context) error
// получить список одобренных валют
// (GET /currencies)
GetCurrencies(ctx echo.Context) error
// обновляет список одобренных валют
// (PATCH /currencies)
UpdateCurrencies(ctx echo.Context) error
// получение лога событий связанных с аккаунтом
// (GET /history)
GetHistory(ctx echo.Context, params GetHistoryParams) error
// публикация лога в истории
// (POST /history)
Add2history(ctx echo.Context) error
// изменить валюту кошелька
// (PATCH /wallet)
ChangeCurrency(ctx echo.Context) error
// запрос на получение ссылки на оплату
// (POST /wallet)
RequestMoney(ctx echo.Context) error
// зачислить деньги на кошелёк
// (PUT /wallet)
PutMoney(ctx echo.Context) error
}
// ServerInterfaceWrapper converts echo contexts to parameters.
type ServerInterfaceWrapper struct {
Handler ServerInterface
}
// DeleteAccount converts echo context to params.
func (w *ServerInterfaceWrapper) DeleteAccount(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.DeleteAccount(ctx)
return err
}
// GetAccount converts echo context to params.
func (w *ServerInterfaceWrapper) GetAccount(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.GetAccount(ctx)
return err
}
// AddAccount converts echo context to params.
func (w *ServerInterfaceWrapper) AddAccount(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.AddAccount(ctx)
return err
}
// DeleteDirectAccount converts echo context to params.
func (w *ServerInterfaceWrapper) DeleteDirectAccount(ctx echo.Context) error {
var err error
// ------------- Path parameter "accountId" -------------
var accountId string
err = runtime.BindStyledParameterWithLocation("simple", false, "accountId", runtime.ParamLocationPath, ctx.Param("accountId"), &accountId)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter accountId: %s", err))
}
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.DeleteDirectAccount(ctx, accountId)
return err
}
// GetDirectAccount converts echo context to params.
func (w *ServerInterfaceWrapper) GetDirectAccount(ctx echo.Context) error {
var err error
// ------------- Path parameter "accountId" -------------
var accountId string
err = runtime.BindStyledParameterWithLocation("simple", false, "accountId", runtime.ParamLocationPath, ctx.Param("accountId"), &accountId)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter accountId: %s", err))
}
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.GetDirectAccount(ctx, accountId)
return err
}
// PaginationAccounts converts echo context to params.
func (w *ServerInterfaceWrapper) PaginationAccounts(ctx echo.Context) error {
var err error
// Parameter object where we will unmarshal all parameters from the context
var params PaginationAccountsParams
// ------------- Optional query parameter "p" -------------
err = runtime.BindQueryParameter("form", false, false, "p", ctx.QueryParams(), &params.P)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p: %s", err))
}
// ------------- Optional query parameter "s" -------------
err = runtime.BindQueryParameter("form", false, false, "s", ctx.QueryParams(), &params.S)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter s: %s", err))
}
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.PaginationAccounts(ctx, params)
return err
}
// RemoveFromCart converts echo context to params.
func (w *ServerInterfaceWrapper) RemoveFromCart(ctx echo.Context) error {
var err error
// Parameter object where we will unmarshal all parameters from the context
var params RemoveFromCartParams
// ------------- Required query parameter "id" -------------
err = runtime.BindQueryParameter("form", true, true, "id", ctx.QueryParams(), &params.Id)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.RemoveFromCart(ctx, params)
return err
}
// Add2cart converts echo context to params.
func (w *ServerInterfaceWrapper) Add2cart(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.Add2cart(ctx)
return err
}
// PayCart converts echo context to params.
func (w *ServerInterfaceWrapper) PayCart(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.PayCart(ctx)
return err
}
// GetCurrencies converts echo context to params.
func (w *ServerInterfaceWrapper) GetCurrencies(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.GetCurrencies(ctx)
return err
}
// UpdateCurrencies converts echo context to params.
func (w *ServerInterfaceWrapper) UpdateCurrencies(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.UpdateCurrencies(ctx)
return err
}
// GetHistory converts echo context to params.
func (w *ServerInterfaceWrapper) GetHistory(ctx echo.Context) error {
var err error
// Parameter object where we will unmarshal all parameters from the context
var params GetHistoryParams
// ------------- Optional query parameter "p" -------------
err = runtime.BindQueryParameter("form", false, false, "p", ctx.QueryParams(), &params.P)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p: %s", err))
}
// ------------- Optional query parameter "s" -------------
err = runtime.BindQueryParameter("form", false, false, "s", ctx.QueryParams(), &params.S)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter s: %s", err))
}
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.GetHistory(ctx, params)
return err
}
// Add2history converts echo context to params.
func (w *ServerInterfaceWrapper) Add2history(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.Add2history(ctx)
return err
}
// ChangeCurrency converts echo context to params.
func (w *ServerInterfaceWrapper) ChangeCurrency(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.ChangeCurrency(ctx)
return err
}
// RequestMoney converts echo context to params.
func (w *ServerInterfaceWrapper) RequestMoney(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.RequestMoney(ctx)
return err
}
// PutMoney converts echo context to params.
func (w *ServerInterfaceWrapper) PutMoney(ctx echo.Context) error {
var err error
// Invoke the callback with all the unmarshalled arguments
err = w.Handler.PutMoney(ctx)
return err
}
// This is a simple interface which specifies echo.Route addition functions which
// are present on both echo.Echo and echo.Group, since we want to allow using
// either of them for path registration
type EchoRouter interface {
CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
}
// RegisterHandlers adds each server route to the EchoRouter.
func RegisterHandlers(router EchoRouter, si ServerInterface) {
RegisterHandlersWithBaseURL(router, si, "")
}
// Registers handlers, and prepends BaseURL to the paths, so that the paths
// can be served under a prefix.
func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) {
wrapper := ServerInterfaceWrapper{
Handler: si,
}
router.DELETE(baseURL+"/account", wrapper.DeleteAccount)
router.GET(baseURL+"/account", wrapper.GetAccount)
router.POST(baseURL+"/account", wrapper.AddAccount)
router.DELETE(baseURL+"/account/:accountId", wrapper.DeleteDirectAccount)
router.GET(baseURL+"/account/:accountId", wrapper.GetDirectAccount)
router.GET(baseURL+"/accounts", wrapper.PaginationAccounts)
router.DELETE(baseURL+"/cart", wrapper.RemoveFromCart)
router.PUT(baseURL+"/cart", wrapper.Add2cart)
router.POST(baseURL+"/cart/pay", wrapper.PayCart)
router.GET(baseURL+"/currencies", wrapper.GetCurrencies)
router.PATCH(baseURL+"/currencies", wrapper.UpdateCurrencies)
router.GET(baseURL+"/history", wrapper.GetHistory)
router.POST(baseURL+"/history", wrapper.Add2history)
router.PATCH(baseURL+"/wallet", wrapper.ChangeCurrency)
router.POST(baseURL+"/wallet", wrapper.RequestMoney)
router.PUT(baseURL+"/wallet", wrapper.PutMoney)
}
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"H4sIAAAAAAAC/+xb3W4bx/V/lcH+/xcJSlFk4tax7hy5H7koELgwctEaxZocSpuSu8zuMq0qCBBF105A",
"2aofwC3Qm96uKK61IrnUK5x5o+Kcmf0eSrIkf6ToTUztzs6cOed3fudjJrtGy+n1HZvbvmds7Bpea5v3",
"TPp5v9VyBraPP/uu0+eub3F68Uerjf/wv5i9fpcbG8YXjbudZufu3SetzhfNVvvuvXt3Pr/XaDaNmuHv",
"9HGE57uWvWXs1YyW6dKU/+/yjrFh/N96tv66Wnx9E8fs1QzLe8C73OfF5Tpm1+PpzE8cp8tNG4cPPO5+",
"dQPR/mx2u/xS4b6Ro/b20hmcJ9/yFgm8qTZn+bznFQT5/UWSXOfVY80G1APTdc0d/Pshbzluu2rAltPr",
"cWnZTFPiiIkX4gCWTAxhCiHMIYA5g3NY4lNYiBETz2nAnc+0lnW56fP2fZq347g90zc2jLbp8zXf6nHd",
"JzcBkjeQat/YNdrca7lW37ccW24EhYYZBAxiCBksYAknYsTgXOxDCFN6PYclvIFIHIjDGsPBMIMIRx+L",
"EUzFSBww8RQimItDcSD2xZhmZRCJISpB7EMEUU3q50WqoXQJ1N4JBOIlg0miWLVuKEct5US49hwC/EMc",
"MvpnH6VHdQ/Zt55j1xlEcIwjpzDH3aGwb2CKu0JzHUAE57jZUwjgHAWEiMESYgjQlhNYiqO6Ucv0vGv4",
"pmt1Ot51cfl4byX6dg1uD3o4sTdotbjndQbdr80dwlvNaPNW17J5O3uC0HAGfvbgyWCH3Igs7Cmz/tJu",
"I+IzpOgmr0h0M0LQOfg3KUWUPMr0tgvrNBt3G81a5gaW7f/iTraGZft8i7vkNgPX5XZrpyjmw0df6nbU",
"c2y+U4U8+WssDuEELS/tLp5DKA4QkhNEzBLOIYQzxI74W53BvwmyxwRrxDKCM0TsiiF+K16JAxypPGgC",
"SziFGCLC3lxiMcR3cILolO+QKGboPRDUpfPMEItiX4wghIUYEngRmlOIYQlnOHEAc/FSHEBYSzArPRCl",
"x/cxuiI5VUzrndWYEkR5XCheKSmV18MbXC0P+Ktao2pyfGTZHUej83/Acc5xafMTUvlQ/iJ5pZNH6Ieo",
"3Ql5Z8DWMqKQIwKYIDPQztRO5uIQTmGJKkL1oJqYeCpGEMNMPEN1o7ZQBLKv2IdTiCBGi0byyQ/01SHq",
"pE6u5hO2Ngee7/S4y9YKYiX0IkYkW04iyTlzFA1iEnYBkVEzvueuJ5XRrDfqDQSo0+e22beMDePzeqOO",
"XtU3/W3ykXUzSyjaFNc1SsWNLyAUzyGQAEBaRsyOcGUmwQVz8Qq1K8ZkZnREEydAXzdkypAkLzXD5V7f",
"sT3pp581GpqAMRJDQv8PhMowXSWzR0EKCHCndxpNjfyvIUTNpRFCGRBiApc36PVMd4fWVEtEivbJGXPI",
"EWN0j8K6aEJzi0g7USUS8RbXBEHCWYoi9D8C5lHq7afo3eSJIRMv4ZRwEFCwGqoYhy47qjP4O5zBFCJS",
"TwSzwnC5xCizVwSnjKhlRvsIahKKpA+5J8UzuH9kGSnVREavExWYJ/g5Mwf+tuNafyXjVgz9a+5fbOWW",
"Y/sqzTH7/a7Vom/XMaRmqe5l6V6yBDHBJbjJaUMh51pAwU/uaD75F8FBBv0VDHFETCgOSmiDf5YFY/TB",
"TIzEj8jhOGMJ4zkzrwBe3/F06deQYoWyLiPVTDRoTukmXaaGXD5En2BEMuleQ/FjyvHHYowIqaDhfru9",
"Eg3ND4IGpYjg3XFIpmmKQKs0rTXfXi0l5PVd9eOr9t6F5IzEjK49JVsQY0kKiWAh151gHBxKSei/C0kG",
"MgotMRWXWcMUgzRFmUUtF3hytFsJOiuY/oHl8laOCfqma/a4z13ccGULQYnLKlbBcG9sUNQyaoZt9qii",
"SvRD2PpuYLlYk/rugNdywCnnj48/ouCzilNeE2sX3W0FtVwcwcrOfS4phbT9VoHrHYOszNH4aEpOGosx",
"5rvLIuxCXdz5yWHuIwiFHyuOc5JeE8k5KiWd63FdzpRIYRFxeAUSS8Q4lXEjKmhkFJElk+oQUFgRzyBk",
"P2OMvAQDeShLPXTK51TKYT651ExfQfXX5pZl05/3k61chuvXKlffL8kkxlSiBahR+veIiSFrUEHW7zrt",
"rIdHsP9uwN2dDPd9I4/vNu+Yg65vbDR01VqFTPdJWXqhrri+p1+/qRPgpt5WbssNSk25ZlPXMUi7i+mP",
"K3ltuT+oL3bf3q/Letah7TZyHektMy2cEWBwTgWEhNwzIvCzlf6atKGzXKfoDg95z/me/8p1eqoZVXIF",
"HXKsi6n6Wj2oK+YQuZRhKWuwYktgtQHit+daysNjRYkhxBntntFPnEc8XZEuiCOsK3Uy5r/NzEaGompj",
"4K8Qv5AiEPFTB3VCsV81eFXnWispIpjqIMmXsljWFRiftSQW0Mbc87902js3cHfrFjuT5LqXA4V6xse5",
"1s4y6Q6mZhh9eKjkpUzgUpbyQrAkDr7eN8lA+kJ1DdNIpLEJRLIhyXBm6h7G1PIU+zCjUAIRVaVyvGxe",
"iMNPKM6oHv4JNVLopOGNnIrOTQiIJ0mBhpT1FIvuT/NdEQqQuZ4nlcDqC+roq7OCcdrJjalBFouxrDLT",
"Dh4OUVlu2dBR2vqh7c6p6I9UNl1NeHR50x9stqb6Q2kek1Xs+X7wpNx+DFnW6KRNPycdrcmuadJtVOnX",
"5Aq6l5qZyhxDpUoy9i/z6pPKmlFb+0D2C0udWMRWRVfJCU1JV1IBug3H+U5utsEaqySX1F0fw5zOd2S3",
"ewnnSuIRo0OfQqc4ffkK3sAM142YojbZGT+kDni9apls3rESJHVdpWgxljZW51Mya07rLwjkhpckPLWx",
"U63m6KKaSCbnN7dJk5eVVKqDoHaR8D2db2Q5dNJqzRxmVFbTLKMn1ZfKjUAw5I8V3g1lv9dajbgn22Bw",
"5bzhqolbXnuZU2bg0VG3PBFT9lfVVKUG38xG3VCPuUN6eez26HcPjJqxef+B8TiXbV9y3H7d5DnNa4Pc",
"adhlNWqxeFxKCksOyWQfI5srp+TkrJESKtNvbVdV+6jfNn1e0u71HPm2FFt2FC0iH5m2Oi7g7SoGj1W7",
"iBKKSvV9HQUiUrctz3fcnYtg+hs15H/F9H9DMa1u1Fyrls6uLLAUJbfAr5pG21wmncmp4pgi1xndBRFH",
"STOJQI5lc7mYXuTwngA8f9xTLYu2U4xflymuovMr1jh6NZd0JkZ0eE/XGcQzmR0nWsMEtHDJR6sPdP/s",
"wljKpeWmX3rYmqTxEG4kadWBpBiqPiJVsga6cpWCdHZTIigTVsDklJVOZtrYRlTQyWwxuWw9cTufUB44",
"lalwmlWn2WmsjgfxkZz0XJ064Zvw02RlmZpPVEUU5eTFqUq3EZjsb2KaP6IbUtnYen7wKyTnmAq9pGYQ",
"z+hqmkpKVcZOp+WFpK9wWKguW1AhE1PuccZgxrJEPb2kodJtMsRL6qieJcMoKyzf95LcrIoFyZRkmTml",
"PPvpgVxypUbey4nIrmF29k2biuWNm0pCvblt2ltJQN65tbxaf+9IRuifUPIqhuq4J58+IQBKkJNM21h5",
"rF71sVgWUQVHuwW+jmRMpUAqy7ALHCVHPYpsVh+8rzHxlBZfZFeuCtRDmL+o+sEhujO3hbweJt3kWLyg",
"KmkBUZl1ArlkehOKflYK2tySuGJWEcsbYeWauHCBhNgiJpJaFm+SpNSkuTZVcamH0oN+S7fpbs2hqncA",
"G43G1S6Z7d1q7tO17D8V3Xrb9/vexvr6luNsdXndHVzNyS/yPnkWe5qBK+2HoJP8XNunLtw2y+Mg62jA",
"nJELRHAMMzEqe08BznHxBlLuQCJBVFTF0wqX0naXJa+XWpHFBVVld6py9DQHmzHEA3VrVBKgaVUnsTJp",
"NqkXdEkydzNSRwulpsvgA4G59jZxpPZ+et7v/7B7Qi3ZYWb9Qm8wKKU0txWKKp6R5kRp+le6FVySQ+MJ",
"pGf5UNN0K14qKl5UMKoVZNITFSM4T7LQXIzOpkgxpJkj148i4CefyP9HRDs8v0M1XG1Q80Eu1RdH2QdJ",
"pr/3eO8/AQAA//8nG+8kJDMAAA==",
}
// GetSwagger returns the content of the embedded swagger specification file
// or error if failed to decode
func decodeSpec() ([]byte, error) {
zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
if err != nil {
return nil, fmt.Errorf("error base64 decoding spec: %s", err)
}
zr, err := gzip.NewReader(bytes.NewReader(zipped))
if err != nil {
return nil, fmt.Errorf("error decompressing spec: %s", err)
}
var buf bytes.Buffer
_, err = buf.ReadFrom(zr)
if err != nil {
return nil, fmt.Errorf("error decompressing spec: %s", err)
}
return buf.Bytes(), nil
}
var rawSpec = decodeSpecCached()
// a naive cached of a decoded swagger spec
func decodeSpecCached() func() ([]byte, error) {
data, err := decodeSpec()
return func() ([]byte, error) {
return data, err
}
}
// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
var res = make(map[string]func() ([]byte, error))
if len(pathToFile) > 0 {
res[pathToFile] = rawSpec
}
return res
}
// GetSwagger returns the Swagger specification corresponding to the generated code
// in this file. The external references of Swagger specification are resolved.
// The logic of resolving external references is tightly connected to "import-mapping" feature.
// Externally referenced files must be embedded in the corresponding golang packages.
// Urls can be supported but this task was out of the scope.
func GetSwagger() (swagger *openapi3.T, err error) {
var resolvePath = PathToRawSpec("")
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
var pathToFile = url.String()
pathToFile = path.Clean(pathToFile)
getSpec, ok := resolvePath[pathToFile]
if !ok {
err1 := fmt.Errorf("path not found: %s", pathToFile)
return nil, err1
}
return getSpec()
}
var specData []byte
specData, err = rawSpec()
if err != nil {
return
}
swagger, err = loader.LoadFromData(specData)
if err != nil {
return
}
return
}

@ -3,7 +3,7 @@ package utils
import (
"net/http"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
)
var clientErrors = map[int]error{

@ -4,8 +4,8 @@ import (
"net/http"
"github.com/labstack/echo/v4"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
var httpStatuses = map[error]int{

@ -8,8 +8,8 @@ import (
"time"
"github.com/golang-jwt/jwt/v5"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
jsonUtil "penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/json"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
jsonUtil "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/json"
)
type JWT[T any] struct {

@ -7,8 +7,8 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
)
func TestJWT(t *testing.T) {

@ -3,8 +3,8 @@ package utils
import (
"fmt"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/validate"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)
func ValidateConfigurationURLs(config *models.ServiceConfiguration) error {

@ -5,7 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/array"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/array"
)
func TestContains(t *testing.T) {

@ -5,7 +5,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/array"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/array"
)
func TestFilter(t *testing.T) {

@ -4,7 +4,6 @@ import (
"context"
"github.com/go-resty/resty/v2"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/convert"
)
func buildRequest(ctx context.Context, settings *RequestSettings) *resty.Request {
@ -18,11 +17,6 @@ func buildRequest(ctx context.Context, settings *RequestSettings) *resty.Request
request.SetBody(settings.Body)
}
if settings.Formdata != nil {
formdata, _ := convert.ObjectToStringMap(settings.Formdata, "formdata")
request.SetFormData(formdata)
}
if settings.QueryParams != nil {
request.SetQueryParams(settings.QueryParams)
}

@ -4,7 +4,7 @@ import (
"bytes"
"net/http"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/json"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/json"
)
func parseResponse[T any](body []byte, response *http.Response) (*T, error) {

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/utils"
)
func TestGetFilledFieldsCount(t *testing.T) {

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/utils"
)
func TestMergeMaps(t *testing.T) {

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/utils"
)
func TestGetRandomString(t *testing.T) {

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/validate"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)
func TestIsStringEmpty(t *testing.T) {

@ -4,7 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"penahub.gitlab.yandexcloud.net/pena-services/pena-social-auth/pkg/validate"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)
func TestValidateURL(t *testing.T) {