diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 15606a5bdd971281657543293db0bcdafca26fc4..b0d92907b11ec36a6dd9ff8414590c9467c38dcf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,4 +16,3 @@ run_tests: when: always reports: junit: report.xml - diff --git a/go.mod b/go.mod index 94c2cfbdd2c37f7bbedc4bcc6f91bd752bd0b492..f53323bb290e119feda2d24ebe935d02cfdc9e38 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,36 @@ module git.perx.ru/perxis/perxis-go go 1.18 require ( + github.com/antonmedv/expr v1.9.0 github.com/go-kit/kit v0.12.0 github.com/golang/protobuf v1.5.2 + github.com/gosimple/slug v1.13.1 github.com/hashicorp/go-multierror v1.1.1 + github.com/hashicorp/golang-lru v0.5.4 github.com/pkg/errors v0.9.1 github.com/rs/xid v1.4.0 - github.com/stretchr/testify v1.7.0 - golang.org/x/net v0.0.0-20210917221730-978cfadd31cf + github.com/stretchr/testify v1.8.0 + go.mongodb.org/mongo-driver v1.11.4 + go.uber.org/zap v1.19.1 + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 google.golang.org/grpc v1.45.0 google.golang.org/protobuf v1.28.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-kit/log v0.2.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.0 // indirect - golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect + github.com/stretchr/objx v0.4.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.7.0 // indirect + golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 2dc3ca61e747e97abd24d179acc4e32112c07a4e..9dea84ab8c222f13374d0a59a405fa290feed61a 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU= +github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -14,8 +19,12 @@ github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -23,6 +32,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= @@ -46,42 +57,99 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= +github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -94,8 +162,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -106,16 +174,24 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -162,11 +238,18 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..7f7248e5208f1691d7e094eaf640f361383faab6 --- /dev/null +++ b/pkg/cache/cache.go @@ -0,0 +1,90 @@ +package cache + +import ( + "errors" + "fmt" + "time" + + lru "github.com/hashicorp/golang-lru" + "go.uber.org/zap" +) + +const ( + defaultCacheSize = 1000 + defaultTTL = 30 * time.Second +) + +var ErrNotFound = errors.New("not found") + +type Cache struct { + cache *lru.Cache + ttl time.Duration + logger *zap.Logger +} + +type item struct { + value interface{} + expiredAt time.Time +} + +func NewCache(size int, ttl time.Duration, opts ...interface{}) *Cache { + if size == 0 { + size = defaultCacheSize + } + if ttl == 0 { + ttl = defaultTTL + } + c, err := lru.New(size) + if err != nil { + panic(err) + } + ch := &Cache{ + cache: c, + ttl: ttl, + logger: zap.NewNop(), + } + + for _, o := range opts { + switch p := o.(type) { + case *zap.Logger: + ch.logger = p + } + } + + ch.logger = ch.logger.Named("Cache") + + return ch +} + +func (c *Cache) Set(key, value interface{}) (err error) { + c.cache.Add(key, &item{value: value, expiredAt: time.Now().Add(c.ttl)}) + c.logger.Debug("Set", zap.String("key", fmt.Sprintf("%v", key)), zap.String("ptr", fmt.Sprintf("%p", value))) + return nil +} + +func (c *Cache) Get(key interface{}) (value interface{}, err error) { + val, ok := c.cache.Get(key) + if ok { + v := val.(*item) + if v.expiredAt.Before(time.Now()) { + c.Remove(key) + c.logger.Debug("Expired", zap.String("key", fmt.Sprintf("%v", key)), zap.String("ptr", fmt.Sprintf("%p", v.value))) + return nil, ErrNotFound + } + c.logger.Debug("Hit", zap.String("key", fmt.Sprintf("%v", key)), zap.String("ptr", fmt.Sprintf("%p", v.value))) + return v.value, nil + } + c.logger.Debug("Miss", zap.String("key", fmt.Sprintf("%v", key))) + return nil, ErrNotFound +} + +func (c *Cache) Remove(key interface{}) (err error) { + present := c.cache.Remove(key) + c.logger.Debug("Remove", zap.String("key", fmt.Sprintf("%v", key))) + + if !present { + err = ErrNotFound + } + + return +} diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go new file mode 100644 index 0000000000000000000000000000000000000000..345a391c09044b61ebac4f869ba26bbcd9def13d --- /dev/null +++ b/pkg/cache/cache_test.go @@ -0,0 +1,82 @@ +package cache + +import ( + "errors" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCache(t *testing.T) { + + t.Run("Simple", func(t *testing.T) { + c := NewCache(10, 0) + + { + val, err := c.Get("test_key") + require.Error(t, err) + assert.True(t, errors.Is(err, ErrNotFound)) + assert.Nil(t, val) + } + { + err := c.Set("test_key", "test_val") + require.NoError(t, err) + + val, err := c.Get("test_key") + require.NoError(t, err) + assert.Equal(t, "test_val", val.(string)) + } + + { + err := c.Remove("test_key") + require.NoError(t, err) + + val, err := c.Get("test_key") + assert.True(t, errors.Is(err, ErrNotFound)) + assert.Nil(t, val) + } + }) + t.Run("Value Evicted", func(t *testing.T) { + c := NewCache(1, 0) + + { + err := c.Set("test_key_1", "test_val_1") + require.NoError(t, err) + + val, err := c.Get("test_key_1") + require.NoError(t, err) + assert.Equal(t, "test_val_1", val.(string)) + } + + { + err := c.Set("test_key_2", "test_val_2") + require.NoError(t, err) + + val, err := c.Get("test_key_1") + assert.True(t, errors.Is(err, ErrNotFound)) + assert.Nil(t, val) + val, err = c.Get("test_key_2") + require.NoError(t, err) + assert.Equal(t, "test_val_2", val.(string)) + } + + }) + t.Run("TTL expired", func(t *testing.T) { + c := NewCache(10, 10*time.Millisecond) + + err := c.Set("test_key", "test_val") + require.NoError(t, err) + + val, err := c.Get("test_key") + require.NoError(t, err) + assert.Equal(t, "test_val", val.(string)) + + time.Sleep(15 * time.Millisecond) + + val, err = c.Get("test_key") + assert.True(t, errors.Is(err, ErrNotFound)) + assert.Nil(t, val) + }) +} diff --git a/pkg/clients/client.go b/pkg/clients/client.go new file mode 100644 index 0000000000000000000000000000000000000000..f38b5acc9b442be39139523bcd316947d5d54fe7 --- /dev/null +++ b/pkg/clients/client.go @@ -0,0 +1,87 @@ +package clients + +// Client - приложение имеющее доÑтуп к API +type Client struct { + // Внутренний идентификатор клиента внутри ÑиÑтемы + ID string `json:"id" bson:"_id"` + + // Идентификатор проÑтранÑтва + SpaceID string `json:"space_id" bson:"-"` + + // Ð˜Ð¼Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ (обÑзательное поле) + Name string `json:"name" bson:"name"` + + // Параметры аутентификации клиента + OAuth *OAuth `json:"oauth,omitempty" bson:"oauth,omitempty"` + TLS *TLS `json:"tls,omitempty" bson:"tls,omitempty"` + APIKey *APIKey `json:"api_key,omitempty" bson:"api_key,omitempty"` + + // ОпиÑание клиента, назначение + Description string `json:"description" bson:"description"` + + // Приложение отключено и не может авторизоватьÑÑ + Disabled *bool `json:"disabled,omitempty" bson:"disabled,omitempty"` + + // Роль Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð² проÑтранÑтве + RoleID string `json:"role_id" bson:"role_id"` +} + +type OAuth struct { + ClientID string `bson:"client_id,omitempty" json:"client_id,omitempty"` // Идентификатор клиента выданные IdP Ñервером, иÑпользуетÑÑ Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ клиента + AuthID string `bson:"auth_id,omitempty" json:"auth_id,omitempty"` // СервиÑ, который иÑпользуетÑÑ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ð¸ клиента + TokenURL string `bson:"token_url,omitempty" json:"token_url,omitempty"` // URL Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ/Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ access token клиента (опционально) + ClientSecret string `bson:"client_secret,omitempty" json:"client_secret,omitempty"` // Секретный Ключ клиента, иÑпользуетÑÑ Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ клиента (опционально) +} + +type APIKey struct { + Key string `bson:"key,omitempty" json:"key,omitempty"` + Rotate bool `bson:"-" json:"rotate,omitempty"` +} + +type TLS struct { + Subject string `json:"subject,omitempty"` + CACert string `json:"ca_cert,omitempty"` + Cert string `json:"cert,omitempty"` + Key string `json:"key,omitempty"` +} + +func (c *Client) SetDisabled(b bool) *Client { + c.Disabled = &b + return c +} + +func (c *Client) IsDisabled() bool { + if c.Disabled != nil && *c.Disabled { + return true + } + return false +} + +func (c Client) Clone() *Client { + clone := &Client{ + ID: c.ID, + SpaceID: c.SpaceID, + Name: c.Name, + Description: c.Description, + RoleID: c.RoleID, + } + + if c.OAuth != nil { + temp := *c.OAuth + clone.OAuth = &temp + } + if c.TLS != nil { + temp := *c.TLS + clone.TLS = &temp + } + if c.APIKey != nil { + temp := *c.APIKey + clone.APIKey = &temp + } + if c.Disabled != nil { + temp := *c.Disabled + clone.Disabled = &temp + } + + return clone +} diff --git a/pkg/clients/mocks/Clients.go b/pkg/clients/mocks/Clients.go new file mode 100644 index 0000000000000000000000000000000000000000..bfeb7e946a1fe50d044479ba785aac15f484ac31 --- /dev/null +++ b/pkg/clients/mocks/Clients.go @@ -0,0 +1,149 @@ +// Code generated by mockery v2.7.4. DO NOT EDIT. + +package mocks + +import ( + context "context" + + clients "git.perx.ru/perxis/perxis-go/pkg/clients" + mock "github.com/stretchr/testify/mock" +) + +// Clients is an autogenerated mock type for the Clients type +type Clients struct { + mock.Mock +} + +// Create provides a mock function with given fields: ctx, client +func (_m *Clients) Create(ctx context.Context, client *clients.Client) (*clients.Client, error) { + ret := _m.Called(ctx, client) + + var r0 *clients.Client + if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) *clients.Client); ok { + r0 = rf(ctx, client) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*clients.Client) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *clients.Client) error); ok { + r1 = rf(ctx, client) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Delete provides a mock function with given fields: ctx, spaceId, id +func (_m *Clients) Delete(ctx context.Context, spaceId string, id string) error { + ret := _m.Called(ctx, spaceId, id) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, spaceId, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Enable provides a mock function with given fields: ctx, spaceId, id, enable +func (_m *Clients) Enable(ctx context.Context, spaceId string, id string, enable bool) error { + ret := _m.Called(ctx, spaceId, id, enable) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) error); ok { + r0 = rf(ctx, spaceId, id, enable) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Get provides a mock function with given fields: ctx, spaceId, id +func (_m *Clients) Get(ctx context.Context, spaceId string, id string) (*clients.Client, error) { + ret := _m.Called(ctx, spaceId, id) + + var r0 *clients.Client + if rf, ok := ret.Get(0).(func(context.Context, string, string) *clients.Client); ok { + r0 = rf(ctx, spaceId, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*clients.Client) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, spaceId, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetBy provides a mock function with given fields: ctx, spaceId, params +func (_m *Clients) GetBy(ctx context.Context, spaceId string, params *clients.GetByParams) (*clients.Client, error) { + ret := _m.Called(ctx, spaceId, params) + + var r0 *clients.Client + if rf, ok := ret.Get(0).(func(context.Context, string, *clients.GetByParams) *clients.Client); ok { + r0 = rf(ctx, spaceId, params) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*clients.Client) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, *clients.GetByParams) error); ok { + r1 = rf(ctx, spaceId, params) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// List provides a mock function with given fields: ctx, spaceId +func (_m *Clients) List(ctx context.Context, spaceId string) ([]*clients.Client, error) { + ret := _m.Called(ctx, spaceId) + + var r0 []*clients.Client + if rf, ok := ret.Get(0).(func(context.Context, string) []*clients.Client); ok { + r0 = rf(ctx, spaceId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*clients.Client) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, spaceId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Update provides a mock function with given fields: ctx, client +func (_m *Clients) Update(ctx context.Context, client *clients.Client) error { + ret := _m.Called(ctx, client) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) error); ok { + r0 = rf(ctx, client) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/pkg/clients/service.go b/pkg/clients/service.go new file mode 100644 index 0000000000000000000000000000000000000000..823de82bdbe720e2a9ff24f069cb94458bf1cf25 --- /dev/null +++ b/pkg/clients/service.go @@ -0,0 +1,37 @@ +package clients + +import ( + "context" +) + +type GetByParams struct { + OAuthClientID string `json:"oauth_client_id,omitempty"` + APIKey string `json:"api_key,omitempty"` + TLSSubject string `json:"tls_subject,omitempty"` +} + +// @microgen grpc, recovering, middleware +// @protobuf git.perx.ru/perxis/perxis-go/proto/clients +// @grpc-addr content.clients.Clients +type Clients interface { + // Create - Ñоздает клиента (приложение) Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ Ñ API + Create(ctx context.Context, client *Client) (created *Client, err error) + + // Get - возвращает клиента по id + Get(ctx context.Context, spaceId, id string) (client *Client, err error) + + // GetBy - возвращает клиента по идентификатору ÑиÑтемы авторизации + GetBy(ctx context.Context, spaceId string, params *GetByParams) (client *Client, err error) + + // List - возвращает ÑпиÑок клиентов Ñозданных в проÑтранÑтве + List(ctx context.Context, spaceId string) (clients []*Client, err error) + + // Update - обновлÑет параметры клиента + Update(ctx context.Context, client *Client) (err error) + + // Delete - удалÑет указанного клиента из проÑтранÑтве + Delete(ctx context.Context, spaceId, id string) (err error) + + // Enable - активирует/деактивирует клиента. Клиент не Ñможет обращатьÑÑ Ðº API платформы + Enable(ctx context.Context, spaceId, id string, enable bool) (err error) +} diff --git a/pkg/clients/transport/client.microgen.go b/pkg/clients/transport/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..633855af4d1f7fe74260ec7b4ece23db6063ebfb --- /dev/null +++ b/pkg/clients/transport/client.microgen.go @@ -0,0 +1,108 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + "errors" + clients "git.perx.ru/perxis/perxis-go/pkg/clients" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +func (set EndpointsSet) Create(arg0 context.Context, arg1 *clients.Client) (res0 *clients.Client, res1 error) { + request := CreateRequest{Client: arg1} + response, res1 := set.CreateEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*CreateResponse).Created, res1 +} + +func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res0 *clients.Client, res1 error) { + request := GetRequest{ + Id: arg2, + SpaceId: arg1, + } + response, res1 := set.GetEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*GetResponse).Client, res1 +} + +func (set EndpointsSet) GetBy(arg0 context.Context, arg1 string, arg2 *clients.GetByParams) (res0 *clients.Client, res1 error) { + request := GetByRequest{ + Config: arg2, + SpaceId: arg1, + } + response, res1 := set.GetByEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*GetByResponse).Client, res1 +} + +func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*clients.Client, res1 error) { + request := ListRequest{SpaceId: arg1} + response, res1 := set.ListEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*ListResponse).Clients, res1 +} + +func (set EndpointsSet) Update(arg0 context.Context, arg1 *clients.Client) (res0 error) { + request := UpdateRequest{Client: arg1} + _, res0 = set.UpdateEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} + +func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (res0 error) { + request := DeleteRequest{ + Id: arg2, + SpaceId: arg1, + } + _, res0 = set.DeleteEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} + +func (set EndpointsSet) Enable(arg0 context.Context, arg1 string, arg2 string, arg3 bool) (res0 error) { + request := EnableRequest{ + Enable: arg3, + Id: arg2, + SpaceId: arg1, + } + _, res0 = set.EnableEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} diff --git a/pkg/clients/transport/endpoints.microgen.go b/pkg/clients/transport/endpoints.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..bf73c784e68eb95f1dc631184046a4016bcca7a1 --- /dev/null +++ b/pkg/clients/transport/endpoints.microgen.go @@ -0,0 +1,16 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import endpoint "github.com/go-kit/kit/endpoint" + +// EndpointsSet implements Clients API and used for transport purposes. +type EndpointsSet struct { + CreateEndpoint endpoint.Endpoint + GetEndpoint endpoint.Endpoint + GetByEndpoint endpoint.Endpoint + ListEndpoint endpoint.Endpoint + UpdateEndpoint endpoint.Endpoint + DeleteEndpoint endpoint.Endpoint + EnableEndpoint endpoint.Endpoint +} diff --git a/pkg/clients/transport/exchanges.microgen.go b/pkg/clients/transport/exchanges.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..2a1a8e65967661b117a20ff62fc6e20837afce2b --- /dev/null +++ b/pkg/clients/transport/exchanges.microgen.go @@ -0,0 +1,58 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import clients "git.perx.ru/perxis/perxis-go/pkg/clients" + +type ( + CreateRequest struct { + Client *clients.Client `json:"client"` + } + CreateResponse struct { + Created *clients.Client `json:"created"` + } + + GetRequest struct { + SpaceId string `json:"space_id"` + Id string `json:"id"` + } + GetResponse struct { + Client *clients.Client `json:"client"` + } + + GetByRequest struct { + SpaceId string `json:"space_id"` + Config *clients.GetByParams `json:"config"` + } + GetByResponse struct { + Client *clients.Client `json:"client"` + } + + ListRequest struct { + SpaceId string `json:"space_id"` + } + ListResponse struct { + Clients []*clients.Client `json:"clients"` + } + + UpdateRequest struct { + Client *clients.Client `json:"client"` + } + // Formal exchange type, please do not delete. + UpdateResponse struct{} + + DeleteRequest struct { + SpaceId string `json:"space_id"` + Id string `json:"id"` + } + // Formal exchange type, please do not delete. + DeleteResponse struct{} + + EnableRequest struct { + SpaceId string `json:"space_id"` + Id string `json:"id"` + Enable bool `json:"enable"` + } + // Formal exchange type, please do not delete. + EnableResponse struct{} +) diff --git a/pkg/clients/transport/grpc/client.microgen.go b/pkg/clients/transport/grpc/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..421a0178c29b691c33085761bc1710e3c4cbe4eb --- /dev/null +++ b/pkg/clients/transport/grpc/client.microgen.go @@ -0,0 +1,68 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/clients/transport" + pb "git.perx.ru/perxis/perxis-go/proto/clients" + grpckit "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" +) + +func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet { + if addr == "" { + addr = "content.clients.Clients" + } + return transport.EndpointsSet{ + CreateEndpoint: grpckit.NewClient( + conn, addr, "Create", + _Encode_Create_Request, + _Decode_Create_Response, + pb.CreateResponse{}, + opts..., + ).Endpoint(), + DeleteEndpoint: grpckit.NewClient( + conn, addr, "Delete", + _Encode_Delete_Request, + _Decode_Delete_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + EnableEndpoint: grpckit.NewClient( + conn, addr, "Enable", + _Encode_Enable_Request, + _Decode_Enable_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + GetByEndpoint: grpckit.NewClient( + conn, addr, "GetBy", + _Encode_GetBy_Request, + _Decode_GetBy_Response, + pb.GetByResponse{}, + opts..., + ).Endpoint(), + GetEndpoint: grpckit.NewClient( + conn, addr, "Get", + _Encode_Get_Request, + _Decode_Get_Response, + pb.GetResponse{}, + opts..., + ).Endpoint(), + ListEndpoint: grpckit.NewClient( + conn, addr, "List", + _Encode_List_Request, + _Decode_List_Response, + pb.ListResponse{}, + opts..., + ).Endpoint(), + UpdateEndpoint: grpckit.NewClient( + conn, addr, "Update", + _Encode_Update_Request, + _Decode_Update_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + } +} diff --git a/pkg/clients/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/clients/transport/grpc/protobuf_endpoint_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..fea7765facc0f5ba2c6f07a5c7016d2e326a5d23 --- /dev/null +++ b/pkg/clients/transport/grpc/protobuf_endpoint_converters.microgen.go @@ -0,0 +1,295 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// Please, do not change functions names! +package transportgrpc + +import ( + "context" + "errors" + + "git.perx.ru/perxis/perxis-go/pkg/clients" + transport "git.perx.ru/perxis/perxis-go/pkg/clients/transport" + pb "git.perx.ru/perxis/perxis-go/proto/clients" + empty "github.com/golang/protobuf/ptypes/empty" +) + +func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetRequest") + } + req := request.(*transport.GetRequest) + return &pb.GetRequest{ + Id: req.Id, + SpaceId: req.SpaceId, + }, nil +} + +func _Encode_List_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListRequest") + } + req := request.(*transport.ListRequest) + return &pb.ListRequest{SpaceId: req.SpaceId}, nil +} + +func _Encode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil DeleteRequest") + } + req := request.(*transport.DeleteRequest) + return &pb.DeleteRequest{ + Id: req.Id, + SpaceId: req.SpaceId, + }, nil +} + +func _Encode_Enable_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil EnableRequest") + } + req := request.(*transport.EnableRequest) + return &pb.EnableRequest{ + Enable: req.Enable, + Id: req.Id, + SpaceId: req.SpaceId, + }, nil +} + +func _Encode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetResponse") + } + resp := response.(*transport.GetResponse) + respClient, err := PtrClientToProto(resp.Client) + if err != nil { + return nil, err + } + return &pb.GetResponse{Client: respClient}, nil +} + +func _Encode_List_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListResponse") + } + resp := response.(*transport.ListResponse) + respClients, err := ListPtrClientToProto(resp.Clients) + if err != nil { + return nil, err + } + return &pb.ListResponse{Clients: respClients}, nil +} + +func _Encode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Encode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Encode_Enable_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetRequest") + } + req := request.(*pb.GetRequest) + return &transport.GetRequest{ + Id: string(req.Id), + SpaceId: string(req.SpaceId), + }, nil +} + +func _Decode_List_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListRequest") + } + req := request.(*pb.ListRequest) + return &transport.ListRequest{SpaceId: string(req.SpaceId)}, nil +} + +func _Decode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil DeleteRequest") + } + req := request.(*pb.DeleteRequest) + return &transport.DeleteRequest{ + Id: string(req.Id), + SpaceId: string(req.SpaceId), + }, nil +} + +func _Decode_Enable_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil EnableRequest") + } + req := request.(*pb.EnableRequest) + return &transport.EnableRequest{ + Enable: bool(req.Enable), + Id: string(req.Id), + SpaceId: string(req.SpaceId), + }, nil +} + +func _Decode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetResponse") + } + resp := response.(*pb.GetResponse) + respClient, err := ProtoToPtrClient(resp.Client) + if err != nil { + return nil, err + } + return &transport.GetResponse{Client: respClient}, nil +} + +func _Decode_List_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListResponse") + } + resp := response.(*pb.ListResponse) + respClients, err := ProtoToListPtrClient(resp.Clients) + if err != nil { + return nil, err + } + return &transport.ListResponse{Clients: respClients}, nil +} + +func _Decode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_Enable_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Encode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil CreateRequest") + } + req := request.(*transport.CreateRequest) + reqClient, err := PtrClientToProto(req.Client) + if err != nil { + return nil, err + } + return &pb.CreateRequest{Client: reqClient}, nil +} + +func _Encode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil UpdateRequest") + } + req := request.(*transport.UpdateRequest) + reqClient, err := PtrClientToProto(req.Client) + if err != nil { + return nil, err + } + return &pb.UpdateRequest{Client: reqClient}, nil +} + +func _Encode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil CreateResponse") + } + resp := response.(*transport.CreateResponse) + respCreated, err := PtrClientToProto(resp.Created) + if err != nil { + return nil, err + } + return &pb.CreateResponse{Created: respCreated}, nil +} + +func _Decode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil CreateRequest") + } + req := request.(*pb.CreateRequest) + reqClient, err := ProtoToPtrClient(req.Client) + if err != nil { + return nil, err + } + return &transport.CreateRequest{Client: reqClient}, nil +} + +func _Decode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil UpdateRequest") + } + req := request.(*pb.UpdateRequest) + reqClient, err := ProtoToPtrClient(req.Client) + if err != nil { + return nil, err + } + return &transport.UpdateRequest{Client: reqClient}, nil +} + +func _Decode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil CreateResponse") + } + resp := response.(*pb.CreateResponse) + respCreated, err := ProtoToPtrClient(resp.Created) + if err != nil { + return nil, err + } + return &transport.CreateResponse{Created: respCreated}, nil +} + +func _Encode_GetBy_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetByRequest") + } + req := request.(*transport.GetByRequest) + pbreq := &pb.GetByRequest{SpaceId: req.SpaceId} + if req != nil && req.Config != nil { + pbreq.ApiKey = req.Config.APIKey + pbreq.TlsSubject = req.Config.TLSSubject + pbreq.OauthClientId = req.Config.OAuthClientID + } + return pbreq, nil +} + +func _Encode_GetBy_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetByResponse") + } + resp := response.(*transport.GetByResponse) + respClient, err := PtrClientToProto(resp.Client) + if err != nil { + return nil, err + } + return &pb.GetByResponse{Client: respClient}, nil +} + +func _Decode_GetBy_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetByRequest") + } + req := request.(*pb.GetByRequest) + return &transport.GetByRequest{ + Config: &clients.GetByParams{ + OAuthClientID: req.OauthClientId, + APIKey: req.ApiKey, + TLSSubject: req.TlsSubject, + }, + SpaceId: string(req.SpaceId), + }, nil +} + +func _Decode_GetBy_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetByResponse") + } + resp := response.(*pb.GetByResponse) + respClient, err := ProtoToPtrClient(resp.Client) + if err != nil { + return nil, err + } + return &transport.GetByResponse{Client: respClient}, nil +} diff --git a/pkg/clients/transport/grpc/protobuf_type_converters.microgen.go b/pkg/clients/transport/grpc/protobuf_type_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..5212c0b5d2dc9c96533f5f64aee9ed58ab241b41 --- /dev/null +++ b/pkg/clients/transport/grpc/protobuf_type_converters.microgen.go @@ -0,0 +1,164 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// It is better for you if you do not change functions names! +// This file will never be overwritten. +package transportgrpc + +import ( + service "git.perx.ru/perxis/perxis-go/pkg/clients" + permission "git.perx.ru/perxis/perxis-go/pkg/permission" + pb "git.perx.ru/perxis/perxis-go/proto/clients" + commonpb "git.perx.ru/perxis/perxis-go/proto/common" +) + +func ListStringToProto(environments []string) ([]string, error) { + return environments, nil +} + +func ProtoToListString(protoEnvironments []string) ([]string, error) { + return protoEnvironments, nil +} + +func PtrClientToProto(client *service.Client) (*pb.Client, error) { + if client == nil { + return nil, nil + } + + var oauth *pb.Client_OAuth + var tls *pb.Client_TLS + var apikey *pb.Client_APIKey + + if client.OAuth != nil { + oauth = &pb.Client_OAuth{ + ClientId: client.OAuth.ClientID, + AuthId: client.OAuth.AuthID, + TokenUrl: client.OAuth.TokenURL, + ClientSecret: client.OAuth.ClientSecret, + } + } + if client.TLS != nil { + tls = &pb.Client_TLS{ + Subject: client.TLS.Subject, + } + } + if client.APIKey != nil { + apikey = &pb.Client_APIKey{ + Key: client.APIKey.Key, + Rotate: client.APIKey.Rotate, + } + } + + return &pb.Client{ + Id: client.ID, + SpaceId: client.SpaceID, + Name: client.Name, + Description: client.Description, + Disabled: client.Disabled, + RoleId: client.RoleID, + //Environments: client.Environments, + //Rules: rules, + Oauth: oauth, + Tls: tls, + ApiKey: apikey, + }, nil +} + +func ProtoToPtrClient(protoClient *pb.Client) (*service.Client, error) { + if protoClient == nil { + return nil, nil + } + + var oauth *service.OAuth + var tls *service.TLS + var apikey *service.APIKey + + if protoClient.Oauth != nil { + oauth = &service.OAuth{ + ClientID: protoClient.Oauth.ClientId, + AuthID: protoClient.Oauth.AuthId, + TokenURL: protoClient.Oauth.TokenUrl, + ClientSecret: protoClient.Oauth.ClientSecret, + } + } + if protoClient.Tls != nil { + tls = &service.TLS{ + Subject: protoClient.Tls.Subject, + } + } + if protoClient.ApiKey != nil { + apikey = &service.APIKey{ + Key: protoClient.ApiKey.Key, + Rotate: protoClient.ApiKey.Rotate, + } + } + + return &service.Client{ + ID: protoClient.Id, + SpaceID: protoClient.SpaceId, + Name: protoClient.Name, + Description: protoClient.Description, + Disabled: protoClient.Disabled, + RoleID: protoClient.RoleId, + OAuth: oauth, + TLS: tls, + APIKey: apikey, + }, nil +} + +func ListPtrClientToProto(clients []*service.Client) ([]*pb.Client, error) { + protoClients := make([]*pb.Client, 0, len(clients)) + for _, c := range clients { + protoClient, _ := PtrClientToProto(c) + protoClients = append(protoClients, protoClient) + } + return protoClients, nil +} + +func ProtoToListPtrClient(protoClients []*pb.Client) ([]*service.Client, error) { + clients := make([]*service.Client, 0, len(protoClients)) + for _, c := range protoClients { + client, _ := ProtoToPtrClient(c) + clients = append(clients, client) + } + return clients, nil +} + +func PtrPermissionRuleToProto(rule *permission.Rule) (*commonpb.Rule, error) { + if rule == nil { + return nil, nil + } + actions := make([]commonpb.Action, 0, len(rule.Actions)) + for _, a := range rule.Actions { + actions = append(actions, commonpb.Action(a)) + } + return &commonpb.Rule{ + CollectionId: rule.CollectionID, + Actions: actions, + Access: commonpb.Access(rule.Access), + HiddenFields: rule.HiddenFields, + ReadonlyFields: rule.ReadonlyFields, + WriteonlyFields: rule.WriteonlyFields, + ReadFilter: rule.ReadFilter, + WriteFilter: rule.WriteFilter, + }, nil +} + +func ProtoToPtrPermissionRule(protoRule *commonpb.Rule) (*permission.Rule, error) { + if protoRule == nil { + return nil, nil + } + actions := make([]permission.Action, 0, len(protoRule.Actions)) + for _, a := range protoRule.Actions { + actions = append(actions, permission.Action(a)) + } + return &permission.Rule{ + CollectionID: protoRule.CollectionId, + Actions: actions, + Access: permission.Access(protoRule.Access), + HiddenFields: protoRule.HiddenFields, + ReadonlyFields: protoRule.ReadonlyFields, + WriteonlyFields: protoRule.WriteonlyFields, + ReadFilter: protoRule.ReadFilter, + WriteFilter: protoRule.WriteFilter, + }, nil +} diff --git a/pkg/clients/transport/grpc/server.microgen.go b/pkg/clients/transport/grpc/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..7408e691dd4fb1e8e772ffe9e530fcd112d02729 --- /dev/null +++ b/pkg/clients/transport/grpc/server.microgen.go @@ -0,0 +1,127 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// DO NOT EDIT. +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/clients/transport" + pb "git.perx.ru/perxis/perxis-go/proto/clients" + grpc "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + context "golang.org/x/net/context" +) + +type clientsServer struct { + create grpc.Handler + get grpc.Handler + getBy grpc.Handler + list grpc.Handler + update grpc.Handler + delete grpc.Handler + enable grpc.Handler + + pb.UnimplementedClientsServer +} + +func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.ClientsServer { + return &clientsServer{ + create: grpc.NewServer( + endpoints.CreateEndpoint, + _Decode_Create_Request, + _Encode_Create_Response, + opts..., + ), + delete: grpc.NewServer( + endpoints.DeleteEndpoint, + _Decode_Delete_Request, + _Encode_Delete_Response, + opts..., + ), + enable: grpc.NewServer( + endpoints.EnableEndpoint, + _Decode_Enable_Request, + _Encode_Enable_Response, + opts..., + ), + get: grpc.NewServer( + endpoints.GetEndpoint, + _Decode_Get_Request, + _Encode_Get_Response, + opts..., + ), + getBy: grpc.NewServer( + endpoints.GetByEndpoint, + _Decode_GetBy_Request, + _Encode_GetBy_Response, + opts..., + ), + list: grpc.NewServer( + endpoints.ListEndpoint, + _Decode_List_Request, + _Encode_List_Response, + opts..., + ), + update: grpc.NewServer( + endpoints.UpdateEndpoint, + _Decode_Update_Request, + _Encode_Update_Response, + opts..., + ), + } +} + +func (S *clientsServer) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateResponse, error) { + _, resp, err := S.create.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.CreateResponse), nil +} + +func (S *clientsServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { + _, resp, err := S.get.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.GetResponse), nil +} + +func (S *clientsServer) GetBy(ctx context.Context, req *pb.GetByRequest) (*pb.GetByResponse, error) { + _, resp, err := S.getBy.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.GetByResponse), nil +} + +func (S *clientsServer) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) { + _, resp, err := S.list.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.ListResponse), nil +} + +func (S *clientsServer) Update(ctx context.Context, req *pb.UpdateRequest) (*empty.Empty, error) { + _, resp, err := S.update.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} + +func (S *clientsServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*empty.Empty, error) { + _, resp, err := S.delete.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} + +func (S *clientsServer) Enable(ctx context.Context, req *pb.EnableRequest) (*empty.Empty, error) { + _, resp, err := S.enable.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} diff --git a/pkg/clients/transport/server.microgen.go b/pkg/clients/transport/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..4c8b6a3e538dd5d1f28c3e7fd2b9f56af54cae8e --- /dev/null +++ b/pkg/clients/transport/server.microgen.go @@ -0,0 +1,77 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + clients "git.perx.ru/perxis/perxis-go/pkg/clients" + endpoint "github.com/go-kit/kit/endpoint" +) + +func Endpoints(svc clients.Clients) EndpointsSet { + return EndpointsSet{ + CreateEndpoint: CreateEndpoint(svc), + DeleteEndpoint: DeleteEndpoint(svc), + EnableEndpoint: EnableEndpoint(svc), + GetByEndpoint: GetByEndpoint(svc), + GetEndpoint: GetEndpoint(svc), + ListEndpoint: ListEndpoint(svc), + UpdateEndpoint: UpdateEndpoint(svc), + } +} + +func CreateEndpoint(svc clients.Clients) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*CreateRequest) + res0, res1 := svc.Create(arg0, req.Client) + return &CreateResponse{Created: res0}, res1 + } +} + +func GetEndpoint(svc clients.Clients) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*GetRequest) + res0, res1 := svc.Get(arg0, req.SpaceId, req.Id) + return &GetResponse{Client: res0}, res1 + } +} + +func GetByEndpoint(svc clients.Clients) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*GetByRequest) + res0, res1 := svc.GetBy(arg0, req.SpaceId, req.Config) + return &GetByResponse{Client: res0}, res1 + } +} + +func ListEndpoint(svc clients.Clients) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*ListRequest) + res0, res1 := svc.List(arg0, req.SpaceId) + return &ListResponse{Clients: res0}, res1 + } +} + +func UpdateEndpoint(svc clients.Clients) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*UpdateRequest) + res0 := svc.Update(arg0, req.Client) + return &UpdateResponse{}, res0 + } +} + +func DeleteEndpoint(svc clients.Clients) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*DeleteRequest) + res0 := svc.Delete(arg0, req.SpaceId, req.Id) + return &DeleteResponse{}, res0 + } +} + +func EnableEndpoint(svc clients.Clients) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*EnableRequest) + res0 := svc.Enable(arg0, req.SpaceId, req.Id, req.Enable) + return &EnableResponse{}, res0 + } +} diff --git a/pkg/collaborators/collaborator.go b/pkg/collaborators/collaborator.go new file mode 100644 index 0000000000000000000000000000000000000000..701d8e85b578dafc2036c281c204720080412ac5 --- /dev/null +++ b/pkg/collaborators/collaborator.go @@ -0,0 +1,7 @@ +package collaborators + +type Collaborator struct { + SpaceID string `bson:"spaceId"` + Subject string `bson:"subject"` + Role string `bson:"role"` +} diff --git a/pkg/collaborators/mocks/Collaborators.go b/pkg/collaborators/mocks/Collaborators.go new file mode 100644 index 0000000000000000000000000000000000000000..6bcd7fb1c462ae85ef01ca50efa6145ce2f8d591 --- /dev/null +++ b/pkg/collaborators/mocks/Collaborators.go @@ -0,0 +1,110 @@ +// Code generated by mockery v2.7.4. DO NOT EDIT. + +package mocks + +import ( + context "context" + + collaborators "git.perx.ru/perxis/perxis-go/pkg/collaborators" + mock "github.com/stretchr/testify/mock" +) + +// Collaborators is an autogenerated mock type for the Collaborators type +type Collaborators struct { + mock.Mock +} + +// Get provides a mock function with given fields: ctx, spaceId, userId +func (_m *Collaborators) Get(ctx context.Context, spaceId, subject string) (string, error) { + ret := _m.Called(ctx, spaceId, subject) + + var r0 string + if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { + r0 = rf(ctx, spaceId, subject) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, spaceId, subject) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListCollaborators provides a mock function with given fields: ctx, spaceId +func (_m *Collaborators) ListCollaborators(ctx context.Context, spaceId string) ([]*collaborators.Collaborator, error) { + ret := _m.Called(ctx, spaceId) + + var r0 []*collaborators.Collaborator + if rf, ok := ret.Get(0).(func(context.Context, string) []*collaborators.Collaborator); ok { + r0 = rf(ctx, spaceId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*collaborators.Collaborator) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, spaceId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListSpaces provides a mock function with given fields: ctx, userId +func (_m *Collaborators) ListSpaces(ctx context.Context, subject string) ([]*collaborators.Collaborator, error) { + ret := _m.Called(ctx, subject) + + var r0 []*collaborators.Collaborator + if rf, ok := ret.Get(0).(func(context.Context, string) []*collaborators.Collaborator); ok { + r0 = rf(ctx, subject) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*collaborators.Collaborator) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, subject) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Remove provides a mock function with given fields: ctx, spaceId, userId +func (_m *Collaborators) Remove(ctx context.Context, spaceId, subject string) error { + ret := _m.Called(ctx, spaceId, subject) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, spaceId, subject) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Set provides a mock function with given fields: ctx, spaceId, userId, role +func (_m *Collaborators) Set(ctx context.Context, spaceId, subject, role string) error { + ret := _m.Called(ctx, spaceId, subject, role) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok { + r0 = rf(ctx, spaceId, subject, role) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/pkg/collaborators/service.go b/pkg/collaborators/service.go new file mode 100644 index 0000000000000000000000000000000000000000..fa56271b89e57a87fc501a11a1ae648f6214a7d5 --- /dev/null +++ b/pkg/collaborators/service.go @@ -0,0 +1,26 @@ +package collaborators + +import ( + "context" +) + +// @microgen grpc, recovering, middleware +// @protobuf git.perx.ru/perxis/perxis-go/proto/collaborators +// @grpc-addr content.collaborators.Collaborators +type Collaborators interface { + + // Set - уÑтанавливает учаÑтие Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² проÑтранÑтве и его роль + Set(ctx context.Context, spaceId, subject, role string) (err error) + + // Get - возвращает роль Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² проÑтранÑтве + Get(ctx context.Context, spaceId, subject string) (role string, err error) + + // Remove - удалÑет учаÑтие Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð² проÑтранÑтве + Remove(ctx context.Context, spaceId, subject string) (err error) + + // ListCollaborators - возвращает учаÑтников проÑтранÑтва и их ролей + ListCollaborators(ctx context.Context, spaceId string) (collaborators []*Collaborator, err error) + + // ListSpaces - возвращает ÑпиÑок проÑтранÑтв Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ + ListSpaces(ctx context.Context, subject string) (spaces []*Collaborator, err error) +} diff --git a/pkg/collaborators/transport/client.microgen.go b/pkg/collaborators/transport/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..eb0dbbe7bc1ea7bad07220fba5cdc6e3b1c9e503 --- /dev/null +++ b/pkg/collaborators/transport/client.microgen.go @@ -0,0 +1,82 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + "errors" + + collaborators "git.perx.ru/perxis/perxis-go/pkg/collaborators" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +func (set EndpointsSet) Set(arg0 context.Context, arg1 string, arg2 string, arg3 string) (res0 error) { + request := SetRequest{ + Role: arg3, + SpaceId: arg1, + Subject: arg2, + } + _, res0 = set.SetEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} + +func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res0 string, res1 error) { + request := GetRequest{ + SpaceId: arg1, + Subject: arg2, + } + response, res1 := set.GetEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*GetResponse).Role, res1 +} + +func (set EndpointsSet) Remove(arg0 context.Context, arg1 string, arg2 string) (res0 error) { + request := RemoveRequest{ + SpaceId: arg1, + Subject: arg2, + } + _, res0 = set.RemoveEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} + +func (set EndpointsSet) ListCollaborators(arg0 context.Context, arg1 string) (res0 []*collaborators.Collaborator, res1 error) { + request := ListCollaboratorsRequest{SpaceId: arg1} + response, res1 := set.ListCollaboratorsEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*ListCollaboratorsResponse).Collaborators, res1 +} + +func (set EndpointsSet) ListSpaces(arg0 context.Context, arg1 string) (res0 []*collaborators.Collaborator, res1 error) { + request := ListSpacesRequest{Subject: arg1} + response, res1 := set.ListSpacesEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*ListSpacesResponse).Spaces, res1 +} diff --git a/pkg/collaborators/transport/endpoints.microgen.go b/pkg/collaborators/transport/endpoints.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..022bf403e3a574918d006786c54cfc854f318915 --- /dev/null +++ b/pkg/collaborators/transport/endpoints.microgen.go @@ -0,0 +1,14 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import endpoint "github.com/go-kit/kit/endpoint" + +// EndpointsSet implements Collaborators API and used for transport purposes. +type EndpointsSet struct { + SetEndpoint endpoint.Endpoint + GetEndpoint endpoint.Endpoint + RemoveEndpoint endpoint.Endpoint + ListCollaboratorsEndpoint endpoint.Endpoint + ListSpacesEndpoint endpoint.Endpoint +} diff --git a/pkg/collaborators/transport/exchanges.microgen.go b/pkg/collaborators/transport/exchanges.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..a51674f40f696f15d1f625fbf7923bdd4879e6b5 --- /dev/null +++ b/pkg/collaborators/transport/exchanges.microgen.go @@ -0,0 +1,44 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import collaborators "git.perx.ru/perxis/perxis-go/pkg/collaborators" + +type ( + SetRequest struct { + SpaceId string `json:"space_id"` + Subject string `json:"subject"` + Role string `json:"role"` + } + // Formal exchange type, please do not delete. + SetResponse struct{} + + GetRequest struct { + SpaceId string `json:"space_id"` + Subject string `json:"subject"` + } + GetResponse struct { + Role string `json:"role"` + } + + RemoveRequest struct { + SpaceId string `json:"space_id"` + Subject string `json:"subject"` + } + // Formal exchange type, please do not delete. + RemoveResponse struct{} + + ListCollaboratorsRequest struct { + SpaceId string `json:"space_id"` + } + ListCollaboratorsResponse struct { + Collaborators []*collaborators.Collaborator `json:"collaborators"` + } + + ListSpacesRequest struct { + Subject string `json:"subject"` + } + ListSpacesResponse struct { + Spaces []*collaborators.Collaborator `json:"spaces"` + } +) diff --git a/pkg/collaborators/transport/grpc/client.microgen.go b/pkg/collaborators/transport/grpc/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..eaf778f878438fc2a44ef980a25a3aed803fcee8 --- /dev/null +++ b/pkg/collaborators/transport/grpc/client.microgen.go @@ -0,0 +1,54 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/collaborators/transport" + pb "git.perx.ru/perxis/perxis-go/proto/collaborators" + grpckit "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" +) + +func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet { + if addr == "" { + addr = "content.collaborators.Collaborators" + } + return transport.EndpointsSet{ + GetEndpoint: grpckit.NewClient( + conn, addr, "Get", + _Encode_Get_Request, + _Decode_Get_Response, + pb.GetResponse{}, + opts..., + ).Endpoint(), + ListCollaboratorsEndpoint: grpckit.NewClient( + conn, addr, "ListCollaborators", + _Encode_ListCollaborators_Request, + _Decode_ListCollaborators_Response, + pb.ListCollaboratorsResponse{}, + opts..., + ).Endpoint(), + ListSpacesEndpoint: grpckit.NewClient( + conn, addr, "ListSpaces", + _Encode_ListSpaces_Request, + _Decode_ListSpaces_Response, + pb.ListSpacesResponse{}, + opts..., + ).Endpoint(), + RemoveEndpoint: grpckit.NewClient( + conn, addr, "Remove", + _Encode_Remove_Request, + _Decode_Remove_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + SetEndpoint: grpckit.NewClient( + conn, addr, "Set", + _Encode_Set_Request, + _Decode_Set_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + } +} diff --git a/pkg/collaborators/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/collaborators/transport/grpc/protobuf_endpoint_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..ec484dfe3b9a8ee1cb38d1d1d03cbfc5ce7a1f8a --- /dev/null +++ b/pkg/collaborators/transport/grpc/protobuf_endpoint_converters.microgen.go @@ -0,0 +1,193 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// Please, do not change functions names! +package transportgrpc + +import ( + "context" + "errors" + + transport "git.perx.ru/perxis/perxis-go/pkg/collaborators/transport" + pb "git.perx.ru/perxis/perxis-go/proto/collaborators" + empty "github.com/golang/protobuf/ptypes/empty" +) + +func _Encode_Set_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil SetRequest") + } + req := request.(*transport.SetRequest) + return &pb.SetRequest{ + Role: req.Role, + SpaceId: req.SpaceId, + Subject: req.Subject, + }, nil +} + +func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetRequest") + } + req := request.(*transport.GetRequest) + return &pb.GetRequest{ + SpaceId: req.SpaceId, + Subject: req.Subject, + }, nil +} + +func _Encode_Remove_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil RemoveRequest") + } + req := request.(*transport.RemoveRequest) + return &pb.RemoveRequest{ + SpaceId: req.SpaceId, + Subject: req.Subject, + }, nil +} + +func _Encode_ListCollaborators_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListCollaboratorsRequest") + } + req := request.(*transport.ListCollaboratorsRequest) + return &pb.ListCollaboratorsRequest{SpaceId: req.SpaceId}, nil +} + +func _Encode_ListSpaces_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListSpacesRequest") + } + req := request.(*transport.ListSpacesRequest) + return &pb.ListSpacesRequest{Subject: req.Subject}, nil +} + +func _Encode_Set_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Encode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetResponse") + } + resp := response.(*transport.GetResponse) + return &pb.GetResponse{Role: resp.Role}, nil +} + +func _Encode_Remove_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Encode_ListCollaborators_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListCollaboratorsResponse") + } + resp := response.(*transport.ListCollaboratorsResponse) + respCollaborators, err := ListPtrCollaboratorToProto(resp.Collaborators) + if err != nil { + return nil, err + } + return &pb.ListCollaboratorsResponse{Collaborators: respCollaborators}, nil +} + +func _Encode_ListSpaces_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListSpacesResponse") + } + resp := response.(*transport.ListSpacesResponse) + respSpaces, err := ListPtrCollaboratorToProto(resp.Spaces) + if err != nil { + return nil, err + } + return &pb.ListSpacesResponse{Spaces: respSpaces}, nil +} + +func _Decode_Set_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil SetRequest") + } + req := request.(*pb.SetRequest) + return &transport.SetRequest{ + Role: string(req.Role), + SpaceId: string(req.SpaceId), + Subject: string(req.Subject), + }, nil +} + +func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetRequest") + } + req := request.(*pb.GetRequest) + return &transport.GetRequest{ + SpaceId: string(req.SpaceId), + Subject: string(req.Subject), + }, nil +} + +func _Decode_Remove_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil RemoveRequest") + } + req := request.(*pb.RemoveRequest) + return &transport.RemoveRequest{ + SpaceId: string(req.SpaceId), + Subject: string(req.Subject), + }, nil +} + +func _Decode_ListCollaborators_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListCollaboratorsRequest") + } + req := request.(*pb.ListCollaboratorsRequest) + return &transport.ListCollaboratorsRequest{SpaceId: string(req.SpaceId)}, nil +} + +func _Decode_ListSpaces_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListSpacesRequest") + } + req := request.(*pb.ListSpacesRequest) + return &transport.ListSpacesRequest{Subject: string(req.Subject)}, nil +} + +func _Decode_Set_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetResponse") + } + resp := response.(*pb.GetResponse) + return &transport.GetResponse{Role: string(resp.Role)}, nil +} + +func _Decode_Remove_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_ListCollaborators_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListCollaboratorsResponse") + } + resp := response.(*pb.ListCollaboratorsResponse) + respCollaborators, err := ProtoToListPtrCollaborator(resp.Collaborators) + if err != nil { + return nil, err + } + return &transport.ListCollaboratorsResponse{Collaborators: respCollaborators}, nil +} + +func _Decode_ListSpaces_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListSpacesResponse") + } + resp := response.(*pb.ListSpacesResponse) + respSpaces, err := ProtoToListPtrCollaborator(resp.Spaces) + if err != nil { + return nil, err + } + return &transport.ListSpacesResponse{Spaces: respSpaces}, nil +} diff --git a/pkg/collaborators/transport/grpc/protobuf_type_converters.microgen.go b/pkg/collaborators/transport/grpc/protobuf_type_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..5752acdf6235ab7b1bd09a46fc9ff3c8467ca7df --- /dev/null +++ b/pkg/collaborators/transport/grpc/protobuf_type_converters.microgen.go @@ -0,0 +1,34 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// It is better for you if you do not change functions names! +// This file will never be overwritten. +package transportgrpc + +import ( + service "git.perx.ru/perxis/perxis-go/pkg/collaborators" + pbcommon "git.perx.ru/perxis/perxis-go/proto/common" +) + +func ListPtrCollaboratorToProto(collaborators []*service.Collaborator) ([]*pbcommon.Collaborator, error) { + protoCollaborators := make([]*pbcommon.Collaborator, 0, len(collaborators)) + for _, c := range collaborators { + protoCollaborators = append(protoCollaborators, &pbcommon.Collaborator{ + SpaceId: c.SpaceID, + Subject: c.Subject, + Role: c.Role, + }) + } + return protoCollaborators, nil +} + +func ProtoToListPtrCollaborator(protoCollaborators []*pbcommon.Collaborator) ([]*service.Collaborator, error) { + collaborators := make([]*service.Collaborator, 0, len(protoCollaborators)) + for _, c := range protoCollaborators { + collaborators = append(collaborators, &service.Collaborator{ + SpaceID: c.SpaceId, + Subject: c.Subject, + Role: c.Role, + }) + } + return collaborators, nil +} diff --git a/pkg/collaborators/transport/grpc/server.microgen.go b/pkg/collaborators/transport/grpc/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..6ece1b8b7d40f2c78d178ea88cf2aeb2ff7bb44f --- /dev/null +++ b/pkg/collaborators/transport/grpc/server.microgen.go @@ -0,0 +1,97 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// DO NOT EDIT. +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/collaborators/transport" + pb "git.perx.ru/perxis/perxis-go/proto/collaborators" + grpc "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + context "golang.org/x/net/context" +) + +type collaboratorsServer struct { + set grpc.Handler + get grpc.Handler + remove grpc.Handler + listCollaborators grpc.Handler + listSpaces grpc.Handler + + pb.UnimplementedCollaboratorsServer +} + +func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.CollaboratorsServer { + return &collaboratorsServer{ + get: grpc.NewServer( + endpoints.GetEndpoint, + _Decode_Get_Request, + _Encode_Get_Response, + opts..., + ), + listCollaborators: grpc.NewServer( + endpoints.ListCollaboratorsEndpoint, + _Decode_ListCollaborators_Request, + _Encode_ListCollaborators_Response, + opts..., + ), + listSpaces: grpc.NewServer( + endpoints.ListSpacesEndpoint, + _Decode_ListSpaces_Request, + _Encode_ListSpaces_Response, + opts..., + ), + remove: grpc.NewServer( + endpoints.RemoveEndpoint, + _Decode_Remove_Request, + _Encode_Remove_Response, + opts..., + ), + set: grpc.NewServer( + endpoints.SetEndpoint, + _Decode_Set_Request, + _Encode_Set_Response, + opts..., + ), + } +} + +func (S *collaboratorsServer) Set(ctx context.Context, req *pb.SetRequest) (*empty.Empty, error) { + _, resp, err := S.set.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} + +func (S *collaboratorsServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { + _, resp, err := S.get.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.GetResponse), nil +} + +func (S *collaboratorsServer) Remove(ctx context.Context, req *pb.RemoveRequest) (*empty.Empty, error) { + _, resp, err := S.remove.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} + +func (S *collaboratorsServer) ListCollaborators(ctx context.Context, req *pb.ListCollaboratorsRequest) (*pb.ListCollaboratorsResponse, error) { + _, resp, err := S.listCollaborators.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.ListCollaboratorsResponse), nil +} + +func (S *collaboratorsServer) ListSpaces(ctx context.Context, req *pb.ListSpacesRequest) (*pb.ListSpacesResponse, error) { + _, resp, err := S.listSpaces.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.ListSpacesResponse), nil +} diff --git a/pkg/collaborators/transport/server.microgen.go b/pkg/collaborators/transport/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..7d0a571d32ebe2ec2a49190a5d79136ff96ba8c1 --- /dev/null +++ b/pkg/collaborators/transport/server.microgen.go @@ -0,0 +1,60 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + + collaborators "git.perx.ru/perxis/perxis-go/pkg/collaborators" + endpoint "github.com/go-kit/kit/endpoint" +) + +func Endpoints(svc collaborators.Collaborators) EndpointsSet { + return EndpointsSet{ + GetEndpoint: GetEndpoint(svc), + ListCollaboratorsEndpoint: ListCollaboratorsEndpoint(svc), + ListSpacesEndpoint: ListSpacesEndpoint(svc), + RemoveEndpoint: RemoveEndpoint(svc), + SetEndpoint: SetEndpoint(svc), + } +} + +func SetEndpoint(svc collaborators.Collaborators) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*SetRequest) + res0 := svc.Set(arg0, req.SpaceId, req.Subject, req.Role) + return &SetResponse{}, res0 + } +} + +func GetEndpoint(svc collaborators.Collaborators) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*GetRequest) + res0, res1 := svc.Get(arg0, req.SpaceId, req.Subject) + return &GetResponse{Role: res0}, res1 + } +} + +func RemoveEndpoint(svc collaborators.Collaborators) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*RemoveRequest) + res0 := svc.Remove(arg0, req.SpaceId, req.Subject) + return &RemoveResponse{}, res0 + } +} + +func ListCollaboratorsEndpoint(svc collaborators.Collaborators) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*ListCollaboratorsRequest) + res0, res1 := svc.ListCollaborators(arg0, req.SpaceId) + return &ListCollaboratorsResponse{Collaborators: res0}, res1 + } +} + +func ListSpacesEndpoint(svc collaborators.Collaborators) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*ListSpacesRequest) + res0, res1 := svc.ListSpaces(arg0, req.Subject) + return &ListSpacesResponse{Spaces: res0}, res1 + } +} diff --git a/pkg/data/crypto.go b/pkg/data/crypto.go new file mode 100644 index 0000000000000000000000000000000000000000..6ec56170d27b43ae708a4277cdc2ae1d08d1a30d --- /dev/null +++ b/pkg/data/crypto.go @@ -0,0 +1,63 @@ +package data + +import ( + "crypto/rand" + "crypto/sha512" + "encoding/base64" + + "git.perx.ru/perxis/perxis-go/pkg/errors" + "golang.org/x/crypto/bcrypt" +) + +func GetHash(value []byte) ([]byte, error) { + return bcrypt.GenerateFromPassword(value, 0) +} + +func CompareHash(value, hash []byte) error { + return bcrypt.CompareHashAndPassword(hash, value) +} + +func GetHashString(value string) (string, error) { + hash, err := GetHash([]byte(value)) + if err != nil { + return "", err + } + return string(hash), nil +} + +func CompareHashString(value, hash string) error { + return CompareHash([]byte(value), []byte(hash)) +} + +func GenerateRandomBytes(n int) []byte { + b := make([]byte, n) + _, err := rand.Read(b) + // Note that err == nil only if we read len(b) bytes. + if err != nil { + panic(errors.Wrap(err, "rand.Read error")) + } + + return b +} + +func GenerateRandomString(n int) string { + const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-" + bytes := GenerateRandomBytes(n) + + for i, b := range bytes { + bytes[i] = letters[b%byte(len(letters))] + } + return string(bytes) +} + +func GenerateRandomStringURLSafe(n int) string { + b := GenerateRandomBytes(n) + return base64.URLEncoding.EncodeToString(b) +} + +func SignValue(signature, value string) string { + hash := sha512.Sum512([]byte(value + "_" + signature)) + encoded := base64.StdEncoding.EncodeToString(hash[:]) + + return encoded +} diff --git a/pkg/data/list.go b/pkg/data/list.go new file mode 100644 index 0000000000000000000000000000000000000000..e15a20cadcc2a22bc9c4797b8fa16413a511b5ab --- /dev/null +++ b/pkg/data/list.go @@ -0,0 +1,186 @@ +package data + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +func Contains[T comparable](s T, arr []T) bool { + + for _, elem := range arr { + if elem == s { + return true + } + } + + return false +} + +func ElementsMatch[T comparable](s1, s2 []T) bool { + if len(s1) != len(s2) { + return false + } + exists := make(map[T]bool) + for _, value := range s1 { + exists[value] = true + } + for _, value := range s2 { + if !exists[value] { + return false + } + } + return true +} + +// Difference returns the elements in `a` that aren't in `b`. +func Difference(a, b []string) []string { + mb := make(map[string]struct{}, len(b)) + for _, x := range b { + mb[x] = struct{}{} + } + var diff []string + for _, x := range a { + if _, found := mb[x]; !found { + diff = append(diff, x) + } + } + return diff +} + +// GetIntersection returns `a` elements that are in `b`. +func GetIntersection[T comparable](a, b []T) []T { + mb := make(map[T]struct{}, len(b)) + for _, x := range b { + mb[x] = struct{}{} + } + var match []T + for _, x := range a { + if _, found := mb[x]; found { + match = append(match, x) + } + } + return match +} + +func GetXOR[T comparable](s1, s2 []T) []T { + + res := make([]T, 0) + + m1 := make(map[T]struct{}, len(s1)) + m2 := make(map[T]struct{}, len(s2)) + + for _, s := range s1 { + m1[s] = struct{}{} + } + + for _, s := range s2 { + if _, ok := m1[s]; !ok { + res = append(res, s) + } + m2[s] = struct{}{} + } + + for _, s := range s1 { + if _, ok := m2[s]; !ok { + res = append(res, s) + } + } + + return res +} + +func SetFromSlice[T comparable](input []T) []T { + s := make([]T, 0, len(input)) + m := make(map[T]bool) + + for _, val := range input { + if _, ok := m[val]; !ok { + m[val] = true + s = append(s, val) + } + } + + return s +} + +func ToSliceOfInterfaces[T any](s []T) []interface{} { + if s == nil { + return nil + } + res := make([]interface{}, len(s)) + for i, e := range s { + res[i] = e + } + return res +} + +func ToSliceOfStrings(s []any) []string { + if s == nil { + return nil + } + res := make([]string, len(s)) + for i, v := range s { + res[i] = fmt.Sprintf("%v", v) + } + return res +} + +func MergeYaml(source, override string) (string, error) { + if source == "" { + return override, nil + } + if override == "" { + return source, nil + } + + var srcYaml map[string]interface{} + err := yaml.Unmarshal([]byte(source), &srcYaml) + if err != nil { + return "", err + } + + var overrideYaml map[string]interface{} + err = yaml.Unmarshal([]byte(override), &overrideYaml) + if err != nil { + return "", err + } + + mergedMap := mergeMaps(srcYaml, overrideYaml) + + mergedString, err := yaml.Marshal(mergedMap) + if err != nil { + return "", err + } + + return string(mergedString), nil +} + +func mergeMaps(a, b map[string]interface{}) map[string]interface{} { + out := make(map[string]interface{}, len(a)) + for k, v := range a { + out[k] = v + } + for k, v := range b { + if v, ok := v.(map[string]interface{}); ok { + if av, ok := out[k]; ok { + if av, ok := av.(map[string]interface{}); ok { + out[k] = mergeMaps(av, v) + continue + } + } + } + + if v, ok := v.([]interface{}); ok { + if av, ok := out[k]; ok { + if av, ok := av.([]interface{}); ok { + out[k] = append(av, v...) + continue + } + } + } + + out[k] = v + } + return out +} diff --git a/pkg/data/list_test.go b/pkg/data/list_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8231aae992e91f09f50bb71fb1e028e148c35993 --- /dev/null +++ b/pkg/data/list_test.go @@ -0,0 +1,141 @@ +package data + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMergeYaml(t *testing.T) { + tests := []struct { + name string + source string + override string + res string + wantErr bool + }{ + {"simple change", + "a: 1\nb: cat", + "a: 2", + "a: 2\nb: cat\n", + false, + }, + {"simple not change", + "a: 1\nb: cat", + "a: 1", + "a: 1\nb: cat\n", + false, + }, + {"object merge", + ` +keyA1: val1 +keyA2: val2 +keyB1: val1 +keyB2: val2`, + ` +keyС1: val1 +keyС2: val2 +keyÐ’1: val1 +keyÐ’2: val2`, + `keyA1: val1 +keyA2: val2 +keyB1: val1 +keyB2: val2 +keyÐ’1: val1 +keyÐ’2: val2 +keyС1: val1 +keyС2: val2 +`, + false, + }, + {"object change", + ` +keyA1: val1 +keyA2: val2 +keyB1: val1 +keyB2: val2`, + ` +keyA1: val10 +keyA2: val20 +keyB1: val10 +keyB2: val20`, + `keyA1: val10 +keyA2: val20 +keyB1: val10 +keyB2: val20 +`, + false, + }, + {"slice of objects merge", + `entries: + - keyA1: val1 + keyA2: val2 + - keyB1: val1 + keyB2: val2 + - val3`, + `entries: + - keyC1: val1 + keyC2: val2 + - keyD1: val1 + keyD2: val2 + - val30`, + `entries: + - keyA1: val1 + keyA2: val2 + - keyB1: val1 + keyB2: val2 + - val3 + - keyC1: val1 + keyC2: val2 + - keyD1: val1 + keyD2: val2 + - val30 +`, + false, + }, + {"slice of objects change", + `entries: + - keyA1: val1 + keyA2: val2 + - keyB1: val1 + keyB2: val2 + - val3`, + `entries: + - keyA1: val10 + keyA2: val20 + - keyB1: val10 + keyB2: val20`, + `entries: + - keyA1: val1 + keyA2: val2 + - keyB1: val1 + keyB2: val2 + - val3 + - keyA1: val10 + keyA2: val20 + - keyB1: val10 + keyB2: val20 +`, + false, + }, + {"invalid string (missing space)", + "a:1", + "a:2", + "", + true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res, err := MergeYaml(tt.source, tt.override) + assert.Equal(t, tt.res, res) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/pkg/data/strings.go b/pkg/data/strings.go new file mode 100644 index 0000000000000000000000000000000000000000..36c3e6943526b17ed821476bc387382a4f72cb51 --- /dev/null +++ b/pkg/data/strings.go @@ -0,0 +1,91 @@ +package data + +import ( + "regexp" + "strings" + "unicode" +) + +var ( + replacer = strings.NewReplacer("?", ".", "*", ".*") +) + +func IsRegExp(s string) bool { + var prev rune + if len(s) < 2 { + return false + } + + if s[0] == '^' || s[len(s)-1] == '$' { + return true + } + + for _, r := range s { + switch r { + case '*', '+': + if prev == '.' { + return true + } + } + prev = r + } + return false +} + +func CamelCase(name string) string { + newName := []rune{} + for i, c := range name { + if i == 0 { + newName = append(newName, unicode.ToLower(c)) + } else { + newName = append(newName, c) + } + } + return string(newName) +} + +func GlobMatch(s string, matches ...string) bool { + + for _, match := range matches { + if match == "" { + return false + } + + if s == match || match == "*" { + return true + } + + ok, err := regexp.MatchString(GlobToRegexp(match), s) + if err != nil { + return false + } + + if ok { + return true + } + } + + return false +} + +func GlobToRegexp(s string) string { + s = strings.TrimSpace(s) + + if s == "*" { + return s + } + // еÑли вначале не звездочка, то ищем Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° (важно Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ð½Ð´ÐµÐºÑов mongo) + if s[0] != '*' { + s = "^" + s + } else { + s = s[1:] + } + // убираем поÑледую звездочку, работает быÑтрее в mongo + if s[len(s)-1] == '*' { + s = s[:len(s)-1] + } else { + s = s + "$" + } + + return replacer.Replace(s) +} diff --git a/pkg/data/strings_test.go b/pkg/data/strings_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a722cd4081ab8e4f04b3ef71369097024a98e320 --- /dev/null +++ b/pkg/data/strings_test.go @@ -0,0 +1,83 @@ +package data + +import ( + "fmt" + "reflect" + "testing" +) + +func TestIsRegExp(t *testing.T) { + tests := []struct { + str string + want bool + }{ + {"^some", true}, + {"some$", true}, + {"some.*", true}, + {"some.+", true}, + {"some*", false}, + {"some+", false}, + {"so.me*", false}, + {"some.", false}, + {"s^om$e", false}, + } + for i, tt := range tests { + t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) { + if got := IsRegExp(tt.str); got != tt.want { + t.Errorf("IsRegExp(\"%s\") = %v, want %v", tt.str, got, tt.want) + } + }) + } +} + +func TestGlobToRegexp(t *testing.T) { + tests := []struct { + name string + args string + want interface{} + }{ + {"from start", "aaa*", "^aaa"}, + {"from end", "*aaa", "aaa$"}, + {"middle", "*aaa*", "aaa"}, + {"start end", "aa*bb", "^aa.*bb$"}, + {"middle and chars", "aa*bb??", "^aa.*bb..$"}, + {"anything", "*", "*"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GlobToRegexp(tt.args); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Match() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGlobMatch(t *testing.T) { + tests := []struct { + name string + s string + glob []string + want bool + }{ + {"from start", "aaateststring", []string{"aaa*"}, true}, + {"from end", "teststringaaa", []string{"*aaa"}, true}, + {"middle", "testaaastring", []string{"*aaa*"}, true}, + {"start end", "aateststringbb", []string{"aa*bb"}, true}, + {"any of", "teststringaa", []string{"aa*", "aa*bb", "*aa"}, true}, + {"none matches", "teststringaa", []string{"aa*", "aa*bb", "*bb"}, false}, + {"middle and chars", "aateststringbbcc", []string{"aa*bb??"}, true}, + {"anything", "teststring", []string{"*"}, true}, + {"false from start", "aateststring", []string{"aaa*"}, false}, + {"false from end", "teststringaa", []string{"*aaa"}, false}, + {"false middle", "testaastring", []string{"*aaa*"}, false}, + {"false start end", "aateststringb", []string{"aa*bb"}, false}, + {"false middle and chars", "ateststringbbcc", []string{"aa*bb??"}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GlobMatch(tt.s, tt.glob...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("Match() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/errors/error.go b/pkg/errors/error.go deleted file mode 100644 index cb8ec386db413ecc1a8f33f6dddd45c29359ea85..0000000000000000000000000000000000000000 --- a/pkg/errors/error.go +++ /dev/null @@ -1,51 +0,0 @@ -package errors - -//import ( -// "errors" -//) - -//var ( -// ErrSpaceRequired = errors.New("space required") -// ErrSpaceHostRequired = errors.New("space host required") -// ErrSpaceNotFound = errors.New("space not found") -// ErrSpaceAlreadyExists = errors.New("space already exists") -//) -// -//var errs = []error{ -// ErrSpaceRequired, -// ErrSpaceHostRequired, -// ErrSpaceNotFound, -// ErrSpaceAlreadyExists, -//} -// -//var errMap map[string]error -// -////func New(text string) error { -//// if errMap == nil { -//// errMap = make(map[string]error) -//// for _, e := range errs { -//// errMap[e.Error()] = e -//// } -//// } -//// -//// if e, ok := errMap[text]; ok { -//// return e -//// } -//// -//// return errors.New(text) -////} -// -//func IsAny(err error, targets ...error) bool { -// for _, t := range targets { -// if errors.Is(err, t) { -// return true -// } -// } -// return false -//} -// -//var ( -// Is = errors.Is -// As = errors.As -// Unwrap = errors.Unwrap -//) diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 952cd8688db8dffe737f63ccba6e1abd4521f715..1b3fe1b2198272d8c17c71fc28ae315b686a4ae5 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -14,59 +14,4 @@ var ( Errorf = errors.Errorf Wrap = errors.Wrap Wrapf = errors.Wrapf - -//Wrap = errors.Wrap -//Wrapf = errors.Wrapf -//WithMessage = errors.WithMessage -//CombineErrors = errors.CombineErrors -////Detail = errors.Detail -////WithDetailf = errors.WithDetailf -////WithHint = errors.WithHint - -//Mark = errors.Mark -//Tags = errors.WithContextTags -// -//CoreDomain = errors.Domain("core") -//StorageDomain = errors.Domain("storage") -//ServiceDomain = errors.Domain("service") -//TransportDomain = errors.Domain("transport") -//EventsDomain = errors.Domain("events") ) - -//func New(msg string) error { -// return &Error{Message: msg} -//} - -//func Trace(err error) error { -// return WithStack(WithID(err)) -//} -// -//func Wrap(err error, msg string) error { -// return WithID(errors.Wrap(err, msg)) -//} -// -//func ErrorBuilder(domain string) func(string) error { -// return func(msg string) error { -// return WithDomain(WithID(New(msg)), domain) -// } -//} -// -//func ErrorfBuilder(domain string) func(format string, args ...interface{}) error { -// return func(format string, args ...interface{}) error { -// return WithDomain(WithID(Errorf(format, args...)), domain) -// } -//} -// -//type Error struct { -// Message string -//} -// -//func (e *Error) Error() string { return e.Message } -//func (e *Error) Is(err error) bool { -// if errr, ok := err.(*Error); ok { -// if e.Message == errr.Message { -// return true -// } -// } -// return false -//} diff --git a/pkg/expr/bench.txt b/pkg/expr/bench.txt new file mode 100644 index 0000000000000000000000000000000000000000..0a8b9b81854561ae9a0ea3484a3fe8c9a544565b --- /dev/null +++ b/pkg/expr/bench.txt @@ -0,0 +1,30 @@ +------ +BenchmarkConvertToMongo +------ +ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ expr `id in [ ...ids ]` в формат bson. + +test 1: + +КоличеÑтво идентификаторов в фильтре: 10_000 +Размер выражениÑ: 230007b (0,2Mb) + +goos: darwin +goarch: amd64 +pkg: github.com/perxteam/perxis/pkg/expr +cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz +BenchmarkConvertToMongo-12 27 44312238 ns/op 13374293 B/op 81067 allocs/op +PASS +ok github.com/perxteam/perxis/pkg/expr 2.572s + +test 2: + +КоличеÑтво идентификаторов в фильтре: 1_000_000 +Размер выражениÑ: 23000007b (21,9Mb) + +goos: darwin +goarch: amd64 +pkg: github.com/perxteam/perxis/pkg/expr +cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz +BenchmarkConvertToMongo-12 1 4142071283 ns/op 1064427296 B/op 7135952 allocs/op +PASS +ok github.com/perxteam/perxis/pkg/expr 4.646s diff --git a/pkg/expr/config.go b/pkg/expr/config.go new file mode 100644 index 0000000000000000000000000000000000000000..628111173d92f81ccdcc5e4c40b1aecc2ff27626 --- /dev/null +++ b/pkg/expr/config.go @@ -0,0 +1,85 @@ +package expr + +import ( + "github.com/antonmedv/expr" + "github.com/antonmedv/expr/conf" +) + +type ExprConfig struct { + options []expr.Option +} + +func (c *ExprConfig) RegisterOption(opt ...expr.Option) { + c.options = append(c.options, opt...) +} + +func (c *ExprConfig) GetConfig(e map[string]interface{}) *conf.Config { + cfg := conf.New(e) + + cfg.Operators = make(map[string][]string) + for _, opt := range c.options { + opt(cfg) + } + + return cfg +} + +var defaultConfig *ExprConfig + +func init() { + defaultConfig = &ExprConfig{} +} + +func RegisterOption(opt ...expr.Option) { + defaultConfig.RegisterOption(opt...) +} + +func GetDefaultConfig(e map[string]interface{}) *conf.Config { + return defaultConfig.GetConfig(e) +} + +//func GetDefaultEnv(e map[string]interface{}) map[string]interface{} { +// return defaultConfig.GetEnv(e) +//} + +func Extend(kv ...interface{}) expr.Option { + e := make(map[string]interface{}) + i := 0 + for { + if i+2 > len(kv) { + break + } + k, v := kv[i], kv[i+1] + name, kOk := k.(string) + if !kOk { + break + } + + e[name] = v + i += 2 + } + return ExtendMap(e) +} + +func ExtendMap(e map[string]interface{}) expr.Option { + return func(c *conf.Config) { + var env map[string]interface{} + var ok bool + if c.Env == nil { + env = make(map[string]interface{}) + } else { + if env, ok = c.Env.(map[string]interface{}); !ok { + panic("only map expr environment is supported") + } + } + for k, v := range e { + if _, ok := env[k]; !ok { + env[k] = v + } + } + c.Strict = true + c.MapEnv = true + c.Env = env + c.Types = conf.CreateTypesTable(c.Env) + } +} diff --git a/pkg/expr/context.go b/pkg/expr/context.go new file mode 100644 index 0000000000000000000000000000000000000000..4049d96f5f62a94219b247b09a4337b474112224 --- /dev/null +++ b/pkg/expr/context.go @@ -0,0 +1,56 @@ +package expr + +import "context" + +type ctxKey struct{} + +type exprCtx struct { + Env map[string]interface{} +} + +func getContext(ctx context.Context) (context.Context, *exprCtx) { + if ctx != nil { + if c, _ := ctx.Value(ctxKey{}).(*exprCtx); c != nil { + return ctx, c + } + } else { + ctx = context.Background() + } + + c := &exprCtx{} + ctx = context.WithValue(ctx, ctxKey{}, c) + return ctx, c +} + +func WithEnv(ctx context.Context, env map[string]interface{}) context.Context { + ctx, c := getContext(ctx) + + if c.Env == nil { + c.Env = make(map[string]interface{}) + } + + for k, v := range env { + c.Env[k] = v + } + + return ctx +} + +func WithEnvKV(ctx context.Context, kv ...interface{}) context.Context { + m := make(map[string]interface{}) + + for i := 0; i < len(kv)/2; i++ { + key, ok := kv[i].(string) + if !ok { + return nil + } + m[key] = kv[i+1] + } + + return WithEnv(ctx, m) +} + +func GetEnv(ctx context.Context) map[string]interface{} { + ctx, c := getContext(ctx) + return c.Env +} diff --git a/pkg/expr/exp.go b/pkg/expr/exp.go new file mode 100644 index 0000000000000000000000000000000000000000..e34b00b8e521dd9c915ee0bed9a6d62e1ac98aee --- /dev/null +++ b/pkg/expr/exp.go @@ -0,0 +1,65 @@ +package expr + +import ( + "fmt" + "strings" + "time" +) + +func EqualString(f, s string) string { + return fmt.Sprintf("%s == '%s'", f, s) +} + +func NotEqualString(f, s string) string { + return fmt.Sprintf("%s != '%s'", f, s) +} + +func EqualNumber(f string, n int) string { + return fmt.Sprintf("%s == %d", f, n) +} + +func NotEqualNumber(f string, n int) string { + return fmt.Sprintf("%s != %d", f, n) +} + +func LessOrEqualNumber(f string, n int) string { + return fmt.Sprintf("%s <= %d", f, n) +} + +func InStringArray(s string, arr []string) string { + arrStr := "'" + strings.Join(arr, "','") + "'" + return fmt.Sprintf("%s in [%s]", s, arrStr) +} + +func NotInStringArray(s string, arr []string) string { + arrStr := "'" + strings.Join(arr, "','") + "'" + return fmt.Sprintf("%s not in [%s]", s, arrStr) +} + +func And(ss ...string) string { + return strings.Join(ss, " && ") +} + +func Or(ss ...string) string { + return strings.Join(ss, " || ") +} + +func Not(s string) string { + return "!" + s +} + +func After(f string, t time.Time) string { + return fmt.Sprintf("%s > Time.Time('%s')", f, t.Format(time.RFC3339)) +} + +func AfterOrEqual(f string, t time.Time) string { + return fmt.Sprintf("%s >= Time.Time('%s')", f, t.Format(time.RFC3339)) +} + +func Before(f string, t time.Time) string { + return fmt.Sprintf("%s < Time.Time('%s')", f, t.Format(time.RFC3339)) +} + +func BeforeOrEqual(f string, t time.Time) string { + return fmt.Sprintf("%s <= Time.Time('%s')", f, t.Format(time.RFC3339)) +} diff --git a/pkg/expr/expr.go b/pkg/expr/expr.go new file mode 100644 index 0000000000000000000000000000000000000000..9c788e71e108e85b483ec71a8700b7fc3bb046be --- /dev/null +++ b/pkg/expr/expr.go @@ -0,0 +1,58 @@ +package expr + +import ( + compiler2 "github.com/antonmedv/expr/compiler" + "github.com/antonmedv/expr/parser" + "github.com/antonmedv/expr/vm" + "golang.org/x/net/context" +) + +const EnvContextKey = "$context" + +func Eval(ctx context.Context, input string, env map[string]interface{}) (interface{}, error) { + tree, err := parser.Parse(input) + if err != nil { + return nil, err + } + + e := GetEnv(ctx) + + if e == nil { + e = make(map[string]interface{}) + } + + for k, v := range env { + e[k] = v + } + + e[EnvContextKey] = ctx + cfg := GetDefaultConfig(e) + + env, _ = cfg.Env.(map[string]interface{}) + + program, err := compiler2.Compile(tree, nil) + if err != nil { + return nil, err + } + + output, err := vm.Run(program, env) + if err != nil { + return nil, err + } + + return output, nil +} + +func EvalKV(ctx context.Context, input string, kv ...interface{}) (interface{}, error) { + m := make(map[string]interface{}) + + for i := 0; i < len(kv)/2; i++ { + key, ok := kv[i].(string) + if !ok { + panic("key should be string") + } + m[key] = kv[i+1] + } + + return Eval(ctx, input, m) +} diff --git a/pkg/expr/format.go b/pkg/expr/format.go new file mode 100644 index 0000000000000000000000000000000000000000..730388d2d1e654549f8dae00332c6958efb26a65 --- /dev/null +++ b/pkg/expr/format.go @@ -0,0 +1,91 @@ +package expr + +import ( + "fmt" + "path" + "regexp" + "strings" + "time" + + "git.perx.ru/perxis/perxis-go/pkg/id" + "github.com/gosimple/slug" +) + +var markersRE = regexp.MustCompile(`{\w+}`) + +func init() { + RegisterOption( + Extend( + "sprintf", Sprintf, + "trim_space", TrimSpace, + "to_upper", ToUpper, + "slugify", Slugify, + "make_path", MakePath, + "normalize_string", func(s string) string { + s = strings.TrimSpace(strings.ToLower(s)) + s = strings.ReplaceAll(s, " ", "_") + s = strings.ReplaceAll(s, ".", "_") + s = strings.ReplaceAll(s, "/", "_") + return strings.ToValidUTF8(s, "") + }, + "replace_markers", ReplaceMarkers, + ), + ) +} + +func Slugify(value interface{}) string { + if s, ok := value.(string); ok { + return slug.Make(s) + } + return "" +} + +func MakePath(base, url interface{}) string { + b, p := "/", "" + if s, ok := base.(string); ok && s != "" { + b = s + } + + if s, ok := url.(string); ok { + p = s + } + + return path.Join(b, p) +} + +func ToUpper(value interface{}) string { + if s, ok := value.(string); ok { + return strings.ToUpper(s) + } + return "" +} + +func TrimSpace(value interface{}) string { + if s, ok := value.(string); ok { + return strings.TrimSpace(s) + } + return "" +} + +func Sprintf(format interface{}, a ...interface{}) string { + if f, ok := format.(string); ok { + return fmt.Sprintf(f, a...) + } + return "" +} + +func ReplaceMarkers(value interface{}) string { + if s, ok := value.(string); ok { + s = markersRE.ReplaceAllStringFunc(s, func(m string) string { + switch { + case m == "{new_id}": + return id.GenerateNewID() + case m == "{now}": + return time.Now().Format(time.RFC3339) + } + return m + }) + return s + } + return "" +} diff --git a/pkg/expr/format_test.go b/pkg/expr/format_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7a0ba4c67e899c058372ca65e050f58e83ec0073 --- /dev/null +++ b/pkg/expr/format_test.go @@ -0,0 +1,45 @@ +package expr + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func TestFormat(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + eval string + env map[string]interface{} + wantB interface{} + }{ + {"sprintf#1", "sprintf(s1, s2)", map[string]interface{}{"s1": "hello %s", "s2": "world"}, "hello world"}, + {"sprintf#2", "sprintf(s1, s2)", map[string]interface{}{"s1": nil, "s2": nil}, ""}, + {"sprintf#3", "sprintf(s1, s2)", map[string]interface{}{"s1": "hello %s", "s2": nil}, "hello %!s(<nil>)"}, + {"to_upper#1", "to_upper(s1)", map[string]interface{}{"s1": "hello"}, "HELLO"}, + {"to_upper#2", "to_upper(s1)", map[string]interface{}{"s1": nil}, ""}, + {"trim_space#1", "trim_space(s1)", map[string]interface{}{"s1": " hel lo wor ld "}, "hel lo wor ld"}, + {"trim_space#2", "trim_space(s1)", map[string]interface{}{"s1": nil}, ""}, + {"slugify#1", "slugify(s1)", map[string]interface{}{"s1": " hello world "}, "hello-world"}, + {"slugify#2", "slugify(s1)", map[string]interface{}{"s1": " привет мир "}, "privet-mir"}, + {"slugify#3", "slugify(s1)", map[string]interface{}{"s1": "privet-mir"}, "privet-mir"}, + {"slugify#4", "slugify(s1)", nil, ""}, + {"make_path#1", "make_path(s1, s2)", map[string]interface{}{"s1": "/pages", "s2": "page-1"}, "/pages/page-1"}, + {"make_path#2", "make_path(s1, s2)", map[string]interface{}{"s1": "/pages", "s2": ""}, "/pages"}, + {"make_path#3", "make_path(s1, s2)", map[string]interface{}{"s1": "", "s2": "pages"}, "/pages"}, + {"make_path#4", "make_path(s1, s2)", map[string]interface{}{"s1": "", "s2": ""}, "/"}, + {"make_path#4", "make_path(s1, s2)", nil, "/"}, + {"make_path#5", "make_path(s1, slugify(s2))", map[string]interface{}{"s1": "/pages", "s2": " привет мир "}, "/pages/privet-mir"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotB, err := Eval(ctx, tt.eval, tt.env) + require.NoError(t, err) + assert.Equal(t, tt.wantB, gotB) + }) + } +} diff --git a/pkg/expr/mongo.go b/pkg/expr/mongo.go new file mode 100644 index 0000000000000000000000000000000000000000..70bd6f05e6f3e5c422c88e193a7e1621eaa2f9e6 --- /dev/null +++ b/pkg/expr/mongo.go @@ -0,0 +1,665 @@ +package expr + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/antonmedv/expr" + "github.com/antonmedv/expr/ast" + compiler2 "github.com/antonmedv/expr/compiler" + "github.com/antonmedv/expr/conf" + "github.com/antonmedv/expr/parser" + "go.mongodb.org/mongo-driver/bson" +) + +var geoTypes = map[string]string{ + "box": "$box", + "polygon": "$polygon", +} + +func ConvertToMongo(ctx context.Context, exp string, env map[string]interface{}, identifierRenameFn func(string) string, ops ...expr.Option) (b bson.M, err error) { + if exp == "" { + return bson.M{}, nil + } + tree, err := parser.Parse(exp) + if err != nil { + return nil, err + } + return convertToMongo(ctx, tree, env, identifierRenameFn, ops...) +} + +func convertToMongo(ctx context.Context, tree *parser.Tree, env map[string]interface{}, identifierRenameFn func(string) string, ops ...expr.Option) (b bson.M, err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("%v", r) + } + }() + + if env == nil { + env = make(map[string]interface{}) + } + + env[EnvContextKey] = ctx + config := GetDefaultConfig(env) + + for _, op := range ops { + op(config) + } + + env = config.Env.(map[string]interface{}) + + if len(config.Visitors) >= 0 { + for _, v := range config.Visitors { + ast.Walk(&tree.Node, v) + } + } + + c := &compiler{tree: tree, env: env, config: config, identifierRenameFn: identifierRenameFn} + v := c.compile(tree.Node) + switch e := v.(type) { + case bson.M: + b = e + case string: + b = bson.M{"$text": bson.M{"$search": e}} + default: + err = fmt.Errorf("invalid expression") + } + return +} + +type compiler struct { + env map[string]interface{} + tree *parser.Tree + config *conf.Config + identifierRenameFn func(string) string +} + +func (c *compiler) eval(node ast.Node) interface{} { + t := &parser.Tree{ + Node: node, + Source: c.tree.Source, + } + prg, err := compiler2.Compile(t, c.config) + if err != nil { + panic(fmt.Sprintf("compile error %s", err.Error())) + } + ret, err := expr.Run(prg, c.env) + if err != nil { + panic(fmt.Sprintf("execution error %s", err.Error())) + } + return ret +} + +func (c *compiler) compile(node ast.Node) interface{} { + switch n := node.(type) { + case *ast.NilNode: + return c.NilNode(n) + case *ast.IdentifierNode: + return c.IdentifierNode(n) + case *ast.IntegerNode: + return c.IntegerNode(n) + case *ast.FloatNode: + return c.FloatNode(n) + case *ast.BoolNode: + return c.BoolNode(n) + case *ast.StringNode: + return c.StringNode(n) + case *ast.ConstantNode: + return c.ConstantNode(n) + case *ast.UnaryNode: + return c.UnaryNode(n) + case *ast.BinaryNode: + return c.BinaryNode(n) + case *ast.MatchesNode: + return c.MatchesNode(n) + case *ast.PropertyNode: + return c.PropertyNode(n) + case *ast.IndexNode: + return c.IndexNode(n) + case *ast.SliceNode: + return c.SliceNode(n) + case *ast.MethodNode: + return c.MethodNode(n) + case *ast.FunctionNode: + return c.FunctionNode(n) + case *ast.BuiltinNode: + return c.BuiltinNode(n) + case *ast.ClosureNode: + return c.ClosureNode(n) + case *ast.PointerNode: + return c.PointerNode(n) + case *ast.ConditionalNode: + return c.ConditionalNode(n) + case *ast.ArrayNode: + return c.ArrayNode(n) + case *ast.MapNode: + return c.MapNode(n) + case *ast.PairNode: + return c.PairNode(n) + default: + panic(fmt.Sprintf("undefined node type (%T)", node)) + } +} + +func (c *compiler) NilNode(node *ast.NilNode) interface{} { + return nil +} + +func (c *compiler) IdentifierNode(node *ast.IdentifierNode) string { + identifier := node.Value + if c.identifierRenameFn != nil { + identifier = c.identifierRenameFn(identifier) + } + return identifier +} + +func (c *compiler) IntegerNode(node *ast.IntegerNode) int { + return node.Value + //t := node.Type() + //if t == nil { + // c.emitPush(node.Value) + // return + //} + // + //switch t.Kind() { + //case reflect.Float32: + // c.emitPush(float32(node.Value)) + //case reflect.Float64: + // c.emitPush(float64(node.Value)) + // + //case reflect.Int: + // c.emitPush(int(node.Value)) + //case reflect.Int8: + // c.emitPush(int8(node.Value)) + //case reflect.Int16: + // c.emitPush(int16(node.Value)) + //case reflect.Int32: + // c.emitPush(int32(node.Value)) + //case reflect.Int64: + // c.emitPush(int64(node.Value)) + // + //case reflect.Uint: + // c.emitPush(uint(node.Value)) + //case reflect.Uint8: + // c.emitPush(uint8(node.Value)) + //case reflect.Uint16: + // c.emitPush(uint16(node.Value)) + //case reflect.Uint32: + // c.emitPush(uint32(node.Value)) + //case reflect.Uint64: + // c.emitPush(uint64(node.Value)) + // + //default: + // c.emitPush(node.Value) + //} +} + +func (c *compiler) FloatNode(node *ast.FloatNode) float64 { + return node.Value +} + +func (c *compiler) BoolNode(node *ast.BoolNode) bool { + return node.Value +} + +func (c *compiler) StringNode(node *ast.StringNode) string { + return node.Value +} + +func (c *compiler) ConstantNode(node *ast.ConstantNode) interface{} { + return node.Value +} + +func (c *compiler) UnaryNode(node *ast.UnaryNode) interface{} { + op := c.compile(node.Node) + + switch node.Operator { + + case "!", "not": + return bson.M{"$not": op} + default: + panic(fmt.Sprintf("unknown operator (%v)", node.Operator)) + } +} + +func (c *compiler) identifier(node ast.Node) string { + switch l := node.(type) { + case *ast.PropertyNode: + return c.PropertyNode(l) + case *ast.IdentifierNode: + return c.IdentifierNode(l) + } + panic(fmt.Sprintf("incorrect identifier node (%v) ", ast.Dump(node))) +} + +func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} { + switch node.Operator { + case "==": + return bson.M{c.identifier(node.Left): c.eval(node.Right)} + + case "!=": + return bson.M{c.identifier(node.Left): bson.M{"$ne": c.eval(node.Right)}} + + case "or", "||": + return bson.M{"$or": bson.A{c.compile(node.Left), c.compile(node.Right)}} + + case "and", "&&": + return bson.M{"$and": bson.A{c.compile(node.Left), c.compile(node.Right)}} + + case "in": + return bson.M{c.identifier(node.Left): bson.M{"$in": c.eval(node.Right)}} + + case "not in": + return bson.M{c.identifier(node.Left): bson.M{"$nin": c.eval(node.Right)}} + + case "<": + return bson.M{c.identifier(node.Left): bson.M{"$lt": c.eval(node.Right)}} + + case ">": + return bson.M{c.identifier(node.Left): bson.M{"$gt": c.eval(node.Right)}} + + case "<=": + return bson.M{c.identifier(node.Left): bson.M{"$lte": c.eval(node.Right)}} + + case ">=": + return bson.M{c.identifier(node.Left): bson.M{"$gte": c.eval(node.Right)}} + + //case "+": + // c.compile(node.Left) + // c.compile(node.Right) + // c.emit(OpAdd) + // + //case "-": + // c.compile(node.Left) + // c.compile(node.Right) + // c.emit(OpSubtract) + // + //case "*": + // c.compile(node.Left) + // c.compile(node.Right) + // c.emit(OpMultiply) + // + //case "/": + // c.compile(node.Left) + // c.compile(node.Right) + // c.emit(OpDivide) + // + //case "%": + // c.compile(node.Left) + // c.compile(node.Right) + // c.emit(OpModulo) + // + //case "**": + // c.compile(node.Left) + // c.compile(node.Right) + // c.emit(OpExponent) + + case "contains": + value, ok := c.eval(node.Right).(string) + if !ok { + panic("contains requires string as an argument") + } + + return bson.M{c.identifier(node.Left): bson.M{"$regex": regexp.QuoteMeta(value)}} + + case "startsWith": + value, ok := c.eval(node.Right).(string) + if !ok { + panic("startsWith requires string as an argument") + } + + return bson.M{c.identifier(node.Left): bson.M{"$regex": fmt.Sprintf("^%s.*", regexp.QuoteMeta(value))}} + + case "endsWith": + value, ok := c.eval(node.Right).(string) + if !ok { + panic("endsWith requires string as an argument") + } + + return bson.M{c.identifier(node.Left): bson.M{"$regex": fmt.Sprintf(".*%s$", regexp.QuoteMeta(value))}} + + case "..": + panic("unsupported range") + + default: + panic(fmt.Sprintf("unknown operator (%v)", node.Operator)) + + } +} + +func (c *compiler) MatchesNode(node *ast.MatchesNode) interface{} { + panic("unsupported match node") + //if node.Regexp != nil { + // c.compile(node.Left) + // c.emit(OpMatchesConst, c.makeConstant(node.Regexp)...) + // return + //} + //c.compile(node.Left) + //c.compile(node.Right) + //c.emit(OpMatches) +} + +func (c *compiler) PropertyNode(node *ast.PropertyNode) string { + v := c.compile(node.Node) + if val, ok := v.(string); ok { + return fmt.Sprintf("%s.%s", val, node.Property) + } + panic(fmt.Sprintf("unsupported property for %v", ast.Dump(node.Node))) +} + +func (c *compiler) IndexNode(node *ast.IndexNode) string { + return fmt.Sprintf("{index-%v}", c.compile(node.Index)) +} + +func (c *compiler) SliceNode(node *ast.SliceNode) interface{} { + panic("unsupported slice node") +} + +func (c *compiler) MethodNode(node *ast.MethodNode) interface{} { + panic("unsupported method node") + //c.compile(node.Node) + //for _, arg := range node.Arguments { + // c.compile(arg) + //} + //c.emit(OpMethod, c.makeConstant(Call{Name: node.Method, Size: len(node.Arguments)})...) +} + +func (c *compiler) FunctionNode(node *ast.FunctionNode) interface{} { + switch node.Name { + case "search", "q": + val := c.compile(node.Arguments[0]) + return bson.M{"$text": bson.M{"$search": val}} + case "near": + v := c.identifier(node.Arguments[0]) + point := c.eval(node.Arguments[1]) + distance := c.eval(node.Arguments[2]) + + if v == "" { + panic("incorrect argument, empty field name") + } + if !strings.HasSuffix(v, ".geometry") { + v += ".geometry" + } + + if _, ok := point.([]interface{}); !ok { + panic("incorrect argument, point must coordinates array") + } + + return bson.M{ + v: bson.M{"$near": bson.D{{Key: "$geometry", Value: map[string]interface{}{"type": "Point", "coordinates": point}}, {Key: "$maxDistance", Value: distance}}}, + } + case "within": + v := c.identifier(node.Arguments[0]) + t := c.eval(node.Arguments[1]) + points := c.eval(node.Arguments[2]) + + if v == "" { + panic("incorrect argument, empty field name") + } + + if !strings.HasSuffix(v, ".geometry") { + v += ".geometry" + } + + typ, ok := t.(string) + if !ok { + panic("incorrect argument, geotype must be string") + } + typ, ok = geoTypes[typ] + if !ok { + panic("incorrect geotype value") + } + + if _, ok := points.([]interface{}); !ok { + panic("incorrect argument, points must be array of coordinates") + } + + return bson.M{ + v: bson.M{"$geoWithin": bson.M{typ: points}}, + } + case "In": + fields := c.identifier(node.Arguments[0]) + if fields == "" { + panic("incorrect argument, empty field name") + } + array, ok := c.eval(node.Arguments[1]).([]interface{}) + if !ok { + array = []interface{}{c.eval(node.Arguments[1])} + } + + return bson.M{fields: bson.M{"$in": array}} + + case "icontains": + v := c.identifier(node.Arguments[0]) + t, ok := c.eval(node.Arguments[1]).(string) + if !ok { + panic("icontains requires string as an argument") + } + return bson.M{v: bson.M{"$regex": regexp.QuoteMeta(t), "$options": "i"}} + + case "istartsWith": + v := c.identifier(node.Arguments[0]) + t, ok := c.eval(node.Arguments[1]).(string) + if !ok { + panic("istartsWith requires string as an argument") + } + return bson.M{v: bson.M{"$regex": fmt.Sprintf("^%s.*", regexp.QuoteMeta(t)), "$options": "i"}} + + case "iendsWith": + v := c.identifier(node.Arguments[0]) + t, ok := c.eval(node.Arguments[1]).(string) + if !ok { + panic("iendsWith requires string as an argument") + } + return bson.M{v: bson.M{"$regex": fmt.Sprintf(".*%s$", regexp.QuoteMeta(t)), "$options": "i"}} + } + panic("unsupported function") + //for _, arg := range node.Arguments { + // c.compile(arg) + //} + //op := OpCall + //if node.Fast { + // op = OpCallFast + //} + //c.emit(op, c.makeConstant(Call{Name: node.Name, Size: len(node.Arguments)})...) +} + +func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} { + panic("unsupported builin node") + //switch node.Name { + //case "len": + // c.compile(node.Arguments[0]) + // c.emit(OpLen) + // c.emit(OpRot) + // c.emit(OpPop) + // + //case "all": + // c.compile(node.Arguments[0]) + // c.emit(OpBegin) + // var loopBreak int + // c.emitLoop(func() { + // c.compile(node.Arguments[1]) + // loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...) + // c.emit(OpPop) + // }) + // c.emit(OpTrue) + // c.patchJump(loopBreak) + // c.emit(OpEnd) + // + //case "none": + // c.compile(node.Arguments[0]) + // c.emit(OpBegin) + // var loopBreak int + // c.emitLoop(func() { + // c.compile(node.Arguments[1]) + // c.emit(OpNot) + // loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...) + // c.emit(OpPop) + // }) + // c.emit(OpTrue) + // c.patchJump(loopBreak) + // c.emit(OpEnd) + // + //case "any": + // c.compile(node.Arguments[0]) + // c.emit(OpBegin) + // var loopBreak int + // c.emitLoop(func() { + // c.compile(node.Arguments[1]) + // loopBreak = c.emit(OpJumpIfTrue, c.placeholder()...) + // c.emit(OpPop) + // }) + // c.emit(OpFalse) + // c.patchJump(loopBreak) + // c.emit(OpEnd) + // + //case "one": + // count := c.makeConstant("count") + // c.compile(node.Arguments[0]) + // c.emit(OpBegin) + // c.emitPush(0) + // c.emit(OpStore, count...) + // c.emitLoop(func() { + // c.compile(node.Arguments[1]) + // c.emitCond(func() { + // c.emit(OpInc, count...) + // }) + // }) + // c.emit(OpLoad, count...) + // c.emitPush(1) + // c.emit(OpEqual) + // c.emit(OpEnd) + // + //case "filter": + // count := c.makeConstant("count") + // c.compile(node.Arguments[0]) + // c.emit(OpBegin) + // c.emitPush(0) + // c.emit(OpStore, count...) + // c.emitLoop(func() { + // c.compile(node.Arguments[1]) + // c.emitCond(func() { + // c.emit(OpInc, count...) + // + // c.emit(OpLoad, c.makeConstant("array")...) + // c.emit(OpLoad, c.makeConstant("i")...) + // c.emit(OpIndex) + // }) + // }) + // c.emit(OpLoad, count...) + // c.emit(OpEnd) + // c.emit(OpArray) + // + //case "map": + // c.compile(node.Arguments[0]) + // c.emit(OpBegin) + // size := c.emitLoop(func() { + // c.compile(node.Arguments[1]) + // }) + // c.emit(OpLoad, size...) + // c.emit(OpEnd) + // c.emit(OpArray) + // + //case "count": + // count := c.makeConstant("count") + // c.compile(node.Arguments[0]) + // c.emit(OpBegin) + // c.emitPush(0) + // c.emit(OpStore, count...) + // c.emitLoop(func() { + // c.compile(node.Arguments[1]) + // c.emitCond(func() { + // c.emit(OpInc, count...) + // }) + // }) + // c.emit(OpLoad, count...) + // c.emit(OpEnd) + // + //default: + // panic(fmt.Sprintf("unknown builtin %v", node.Name)) + //} +} + +//func (c *compiler) emitLoop(body func()) []byte { +// i := c.makeConstant("i") +// size := c.makeConstant("size") +// array := c.makeConstant("array") +// +// c.emit(OpLen) +// c.emit(OpStore, size...) +// c.emit(OpStore, array...) +// c.emitPush(0) +// c.emit(OpStore, i...) +// +// cond := len(c.bytecode) +// c.emit(OpLoad, i...) +// c.emit(OpLoad, size...) +// c.emit(OpLess) +// end := c.emit(OpJumpIfFalse, c.placeholder()...) +// c.emit(OpPop) +// +// body() +// +// c.emit(OpInc, i...) +// c.emit(OpJumpBackward, c.calcBackwardJump(cond)...) +// +// c.patchJump(end) +// c.emit(OpPop) +// +// return size +//} + +func (c *compiler) ClosureNode(node *ast.ClosureNode) interface{} { + return c.compile(node.Node) +} + +func (c *compiler) PointerNode(node *ast.PointerNode) interface{} { + panic("unsupported pointer node") + //c.emit(OpLoad, c.makeConstant("array")...) + //c.emit(OpLoad, c.makeConstant("i")...) + //c.emit(OpIndex) +} + +func (c *compiler) ConditionalNode(node *ast.ConditionalNode) interface{} { + panic("unsupported conditional node") + //c.compile(node.Cond) + //otherwise := c.emit(OpJumpIfFalse, c.placeholder()...) + // + //c.emit(OpPop) + //c.compile(node.Exp1) + //end := c.emit(OpJump, c.placeholder()...) + // + //c.patchJump(otherwise) + //c.emit(OpPop) + //c.compile(node.Exp2) + // + //c.patchJump(end) +} + +func (c *compiler) ArrayNode(node *ast.ArrayNode) interface{} { + panic("unsupported array node") + //for _, node := range node.Nodes { + // c.compile(node) + //} + // + //c.emitPush(len(node.Nodes)) + //c.emit(OpArray) +} + +func (c *compiler) MapNode(node *ast.MapNode) interface{} { + panic("unsupported map node") + //for _, pair := range node.Pairs { + // c.compile(pair) + //} + // + //c.emitPush(len(node.Pairs)) + //c.emit(OpMap) +} + +func (c *compiler) PairNode(node *ast.PairNode) interface{} { + panic("unsupported pair node") + //c.compile(node.Key) + //c.compile(node.Value) +} diff --git a/pkg/expr/mongo_test.go b/pkg/expr/mongo_test.go new file mode 100644 index 0000000000000000000000000000000000000000..46f92b551086ae7ad1d5336f35c98d3154b4253d --- /dev/null +++ b/pkg/expr/mongo_test.go @@ -0,0 +1,86 @@ +package expr + +import ( + "fmt" + "testing" + "time" + + "git.perx.ru/perxis/perxis-go/pkg/id" + "github.com/antonmedv/expr" + "github.com/antonmedv/expr/ast" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.mongodb.org/mongo-driver/bson" + "golang.org/x/net/context" +) + +func TestConvertToMongo(t *testing.T) { + now := time.Now() + dt, _ := time.Parse("2006-01-02", "2021-08-31") + tm, _ := time.Parse(time.RFC3339, now.Format(time.RFC3339)) + ctx := context.Background() + + tests := []struct { + name string + eval string + env map[string]interface{} + wantB bson.M + wantErr bool + }{ + {"equal", "s == 3", nil, bson.M{"s": 3}, false}, + {"in array", "s in [1,2,3]", nil, bson.M{"s": bson.M{"$in": []interface{}{1, 2, 3}}}, false}, + {"contains", "s contains 'some'", nil, bson.M{"s": bson.M{"$regex": "some"}}, false}, + {"contains with . + () $ {} ^", "value contains 'something with . + () $ {} ^'", nil, bson.M{"value": bson.M{"$regex": "something with \\. \\+ \\(\\) \\$ \\{\\} \\^"}}, false}, + {"startsWith", "s startsWith 'some'", nil, bson.M{"s": bson.M{"$regex": "^some.*"}}, false}, + {"startsWith . + () $ {} ^", "s startsWith '. + () $ {} ^'", nil, bson.M{"s": bson.M{"$regex": "^\\. \\+ \\(\\) \\$ \\{\\} \\^.*"}}, false}, + {"endsWith", "s endsWith 'some'", nil, bson.M{"s": bson.M{"$regex": ".*some$"}}, false}, + {"endsWith . + () $ {} ^", "s endsWith '. + () $ {} ^'", nil, bson.M{"s": bson.M{"$regex": ".*\\. \\+ \\(\\) \\$ \\{\\} \\^$"}}, false}, + {"icontains", "icontains(s, 'some')", nil, bson.M{"s": bson.M{"$regex": "some", "$options": "i"}}, false}, + {"icontains with . + () $ {} ^", "icontains (value, 'something with . + () $ {} ^')", nil, bson.M{"value": bson.M{"$regex": "something with \\. \\+ \\(\\) \\$ \\{\\} \\^", "$options": "i"}}, false}, + {"istartsWith", "istartsWith(s, 'Some')", nil, bson.M{"s": bson.M{"$regex": "^Some.*", "$options": "i"}}, false}, + {"istartsWith . + () $ {} ^ . + () $ {} ^", "istartsWith(s, '. + () $ {} ^')", nil, bson.M{"s": bson.M{"$regex": "^\\. \\+ \\(\\) \\$ \\{\\} \\^.*", "$options": "i"}}, false}, + {"iendsWith", "iendsWith(s, 'some')", nil, bson.M{"s": bson.M{"$regex": ".*some$", "$options": "i"}}, false}, + {"iendsWith . + () $ {} ^", "iendsWith(s,'. + () $ {} ^')", nil, bson.M{"s": bson.M{"$regex": ".*\\. \\+ \\(\\) \\$ \\{\\} \\^$", "$options": "i"}}, false}, + {"or", "s==2 || s > 10", nil, bson.M{"$or": bson.A{bson.M{"s": 2}, bson.M{"s": bson.M{"$gt": 10}}}}, false}, + {"search", "search('some') || s > 10", nil, bson.M{"$or": bson.A{bson.M{"$text": bson.M{"$search": "some"}}, bson.M{"s": bson.M{"$gt": 10}}}}, false}, + {"vars:or", "s== a + 2 || s > a + 10", map[string]interface{}{"a": 100}, bson.M{"$or": bson.A{bson.M{"s": 102}, bson.M{"s": bson.M{"$gt": 110}}}}, false}, + {"near", "near(a, [55.5, 37.5], 1000)", map[string]interface{}{"a": []interface{}{55, 37}}, bson.M{"a.geometry": bson.M{"$near": bson.D{{Key: "$geometry", Value: map[string]interface{}{"coordinates": []interface{}{55.5, 37.5}, "type": "Point"}}, {Key: "$maxDistance", Value: 1000}}}}, false}, + {"within", "within(a, 'box', [[54.54, 36.36], [55.55, 37.37]])", map[string]interface{}{"a": []interface{}{55, 37}}, bson.M{"a.geometry": bson.M{"$geoWithin": bson.M{"$box": []interface{}{[]interface{}{54.54, 36.36}, []interface{}{55.55, 37.37}}}}}, false}, + {"time", "d > Time.Date('2021-08-31')", nil, bson.M{"d": bson.M{"$gt": dt}}, false}, + {"time", fmt.Sprintf("d > Time.Time('%s')", now.Format(time.RFC3339)), nil, bson.M{"d": bson.M{"$gt": tm}}, false}, + {"in", "In(s, [1,2,3])", nil, bson.M{"s": bson.M{"$in": []interface{}{1, 2, 3}}}, false}, + {"in", "In(s, 1)", nil, bson.M{"s": bson.M{"$in": []interface{}{1}}}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotB, err := ConvertToMongo(ctx, tt.eval, tt.env, nil) + require.NoError(t, err) + assert.Equal(t, tt.wantB, gotB) + }) + } +} + +func BenchmarkConvertToMongo(b *testing.B) { + const idsNum = 1_000_000 + ctx := context.Background() + + ids := make([]string, idsNum) + for i := 0; i < idsNum; i++ { + ids[i] = id.GenerateNewID() + } + exp := InStringArray("id", ids) + //fmt.Println(len(exp)) + + for i := 0; i < b.N; i++ { + ConvertToMongo(ctx, exp, nil, nil, expr.Patch(&testVisitor{})) + } +} + +type testVisitor struct{} + +func (v *testVisitor) Enter(node *ast.Node) {} +func (v *testVisitor) Exit(node *ast.Node) { + if n, ok := (*node).(*ast.IdentifierNode); ok { + n.Value = "some" + "." + n.Value + } +} diff --git a/pkg/expr/slice.go b/pkg/expr/slice.go new file mode 100644 index 0000000000000000000000000000000000000000..d286e146b79a4f430249db5ae13565ffe2da3764 --- /dev/null +++ b/pkg/expr/slice.go @@ -0,0 +1,54 @@ +package expr + +const ( + Unknown uint8 = iota + String + Int + Float +) + +func init() { + RegisterOption( + Extend("In", In), + ) +} + +func In(v1, v2 interface{}) bool { + if v1 == nil || v2 == nil { + return false + } + + s1, ok1 := v1.([]interface{}) + s2, ok2 := v2.([]interface{}) + + switch { + case ok1 && ok2: + return sliceInSlice1(s1, s2) + case ok1 && !ok2: + return valueInSlice1(v2, s1) + case !ok1 && ok2: + return valueInSlice1(v1, s2) + case !ok1 && !ok2: + return v1 == v2 + } + + return false +} + +func sliceInSlice1(s1, s2 []interface{}) bool { + for _, v1 := range s1 { + if valueInSlice1(v1, s2) { + return true + } + } + return false +} + +func valueInSlice1(v interface{}, s []interface{}) bool { + for _, e := range s { + if e == v { + return true + } + } + return false +} diff --git a/pkg/expr/slice_test.go b/pkg/expr/slice_test.go new file mode 100644 index 0000000000000000000000000000000000000000..533c2bebbd29677dfef9951cdbd90596d3c1d027 --- /dev/null +++ b/pkg/expr/slice_test.go @@ -0,0 +1,62 @@ +package expr + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func TestSlice(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + eval string + env map[string]interface{} + wantB interface{} + }{ + // nil + {"nil#1", "In(a1, a2)", map[string]interface{}{"a1": nil, "a2": nil}, false}, + {"nil#2", "In(a1, a2)", map[string]interface{}{"a1": nil, "a2": []interface{}{"a", "b"}}, false}, + {"nil#3", "In(a1, a2)", map[string]interface{}{"a1": nil, "a2": []interface{}{1, 2}}, false}, + {"nil#4", "In(a1, a2)", map[string]interface{}{"a1": nil, "a2": []interface{}{1.1, 1.2}}, false}, + {"nil#5", "In(a1, a2)", map[string]interface{}{"a1": nil, "a2": "a"}, false}, + {"nil#6", "In(a1, a2)", map[string]interface{}{"a1": nil, "a2": 1}, false}, + {"nil#7", "In(a1, a2)", map[string]interface{}{"a1": nil, "a2": 1.1}, false}, + // strings + {"strings#1", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{"a", "b"}, "a2": []interface{}{"a", "b"}}, true}, + {"strings#2", "In(a1, a2)", map[string]interface{}{"a1": "b", "a2": []interface{}{"a", "b"}}, true}, + {"strings#3", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{"a", "b"}, "a2": "b"}, true}, + {"strings#4", "In(a1, a2)", map[string]interface{}{"a1": "b", "a2": "b"}, true}, + {"strings#5", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{"a", "b"}, "a2": nil}, false}, + {"strings#6", "In(a1, a2)", map[string]interface{}{"a1": "c", "a2": []interface{}{"a", "b"}}, false}, + {"strings#7", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{"a", "b"}, "a2": "c"}, false}, + {"strings#8", "In(a1, a2)", map[string]interface{}{"a1": "a", "a2": "b"}, false}, + {"strings#9", "In(a1, a2)", map[string]interface{}{"a1": "1", "a2": 1}, false}, + // numbers + {"numbers#1", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{1, 2}, "a2": []interface{}{1, 2}}, true}, + {"numbers#5", "In(a1, a2)", map[string]interface{}{"a1": 1, "a2": []interface{}{1, 2}}, true}, + {"numbers#6", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{1, 2}, "a2": 2}, true}, + {"numbers#7", "In(a1, a2)", map[string]interface{}{"a1": 3, "a2": []interface{}{1, 2}}, false}, + {"numbers#8", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{1, 2}, "a2": 3}, false}, + {"numbers#9", "In(a1, a1)", map[string]interface{}{"a1": []interface{}{1.1, 2.1}, "a2": []interface{}{1.1, 2.1}}, true}, + {"numbers#10", "In(a1, a2)", map[string]interface{}{"a1": 1.1, "a2": []interface{}{1.1, 2.1}}, true}, + {"numbers#11", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{1.1, 2.1}, "a2": 2.1}, true}, + {"numbers#12", "In(a1, a2)", map[string]interface{}{"a1": 3.1, "a2": []interface{}{1.1, 2.1}}, false}, + {"numbers#13", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{1.1, 2.1}, "a2": 3.1}, false}, + // mix types + {"mix#1", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{"a", "b"}, "a2": map[string]interface{}{"a": "a", "b": "b"}}, false}, + {"mix#2", "In(a1, a2)", map[string]interface{}{"a1": 2, "a2": "2"}, false}, + {"mix#3", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{}, "a2": nil}, false}, + {"mix#4", "In(a1, a2)", map[string]interface{}{"a1": []interface{}{"1", "2"}, "a2": []interface{}{1, 2}}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotB, err := Eval(ctx, tt.eval, tt.env) + require.NoError(t, err) + assert.Equal(t, tt.wantB, gotB) + }) + } +} diff --git a/pkg/expr/string.go b/pkg/expr/string.go new file mode 100644 index 0000000000000000000000000000000000000000..0e857b41f3d909437f1e8296fd48e866fdac9104 --- /dev/null +++ b/pkg/expr/string.go @@ -0,0 +1,50 @@ +package expr + +import "strings" + +func init() { + RegisterOption( + Extend("icontains", IContains), + Extend("istartsWith", IStartsWith), + Extend("iendsWith", IEndsWith), + ) +} + +func IContains(v1, v2 interface{}) bool { + s1, ok := v1.(string) + if !ok { + return false + } + s2, ok := v2.(string) + if !ok { + return false + } + + return strings.Contains(strings.ToLower(s1), strings.ToLower(s2)) +} + +func IStartsWith(v1, v2 interface{}) bool { + s1, ok := v1.(string) + if !ok { + return false + } + s2, ok := v2.(string) + if !ok { + return false + } + + return strings.HasPrefix(strings.ToLower(s1), strings.ToLower(s2)) +} + +func IEndsWith(v1, v2 interface{}) bool { + s1, ok := v1.(string) + if !ok { + return false + } + s2, ok := v2.(string) + if !ok { + return false + } + + return strings.HasSuffix(strings.ToLower(s1), strings.ToLower(s2)) +} diff --git a/pkg/expr/string_test.go b/pkg/expr/string_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ee8e69fada36dfb394d2dd10d3c54d9c950033bf --- /dev/null +++ b/pkg/expr/string_test.go @@ -0,0 +1,84 @@ +package expr + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIContains(t *testing.T) { + tests := []struct { + name string + s1 interface{} + s2 interface{} + want bool + }{ + {name: "nil#1", s1: nil, s2: nil, want: false}, + {name: "nil#2", s1: nil, s2: "hello", want: false}, + {name: "nil#3", s1: "hello", s2: nil, want: false}, + {name: "notstring#1", s1: map[string]interface{}{"a1": []interface{}{"a", "b"}}, s2: "hello", want: false}, + {name: "notstring#2", s1: 1, s2: "hello", want: false}, + {name: "correct#1", s1: "Abc", s2: "abc", want: true}, + {name: "correct#2", s1: "HELLO", s2: "hello", want: true}, + {name: "correct#3", s1: "abcDeFGh", s2: "dEFg", want: true}, + {name: "correct#4", s1: "heLLOworLD", s2: "ORL", want: true}, + {name: "incorrect#1", s1: "HELLO", s2: "elloo", want: false}, + {name: "incorrect#2", s1: "world", s2: "Word", want: false}, + {name: "incorrect#3", s1: "heLLOworLD", s2: "OOo", want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, IContains(tt.s1, tt.s2), "Icontains(%v, %v)", tt.s1, tt.s2) + }) + } +} + +func TestIEndsWith(t *testing.T) { + tests := []struct { + name string + s1 interface{} + s2 interface{} + want bool + }{ + {name: "nil#1", s1: nil, s2: nil, want: false}, + {name: "nil#2", s1: nil, s2: "hello", want: false}, + {name: "nil#3", s1: "hello", s2: nil, want: false}, + {name: "notstring#1", s1: map[string]interface{}{"a1": []interface{}{"a", "b"}}, s2: "hello", want: false}, + {name: "notstring#2", s1: 1, s2: "hello", want: false}, + {name: "correct#1", s1: "helloabc", s2: "Abc", want: true}, + {name: "correct#2", s1: "worldhello", s2: "HELLO", want: true}, + {name: "correct#3", s1: "abcDeFGh", s2: "FGH", want: true}, + {name: "incorrect#1", s1: "ello", s2: "HeLLo", want: false}, + {name: "incorrect#2", s1: "Word", s2: "world", want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, IEndsWith(tt.s1, tt.s2), "IendsWith(%v, %v)", tt.s1, tt.s2) + }) + } +} + +func TestIStartsWith(t *testing.T) { + tests := []struct { + name string + s1 interface{} + s2 interface{} + want bool + }{ + {name: "nil#1", s1: nil, s2: nil, want: false}, + {name: "nil#2", s1: nil, s2: "hello", want: false}, + {name: "nil#3", s1: "hello", s2: nil, want: false}, + {name: "notstring#1", s1: map[string]interface{}{"a1": []interface{}{"a", "b"}}, s2: "hello", want: false}, + {name: "notstring#2", s1: 1, s2: "hello", want: false}, + {name: "correct#1", s1: "helloabc", s2: "he", want: true}, + {name: "correct#2", s1: "worldhello", s2: "WOR", want: true}, + {name: "correct#3", s1: "abcDeFGh", s2: "ABC", want: true}, + {name: "incorrect#1", s1: "ello", s2: "HeLLo", want: false}, + {name: "incorrect#2", s1: "Word", s2: "world", want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, IStartsWith(tt.s1, tt.s2), "IstartsWith(%v, %v)", tt.s1, tt.s2) + }) + } +} diff --git a/pkg/expr/time.go b/pkg/expr/time.go new file mode 100644 index 0000000000000000000000000000000000000000..740da301a70b65a15f83ac70b1f512c47c171b35 --- /dev/null +++ b/pkg/expr/time.go @@ -0,0 +1,79 @@ +package expr + +import ( + "time" + + "github.com/antonmedv/expr" +) + +const DefaultTimeLayout = time.RFC3339 +const DefaultDateLayout = "2006-01-02" + +type TimeExpr struct{} + +func init() { + RegisterOption( + Extend("Time", TimeExpr{}), + expr.Operator("==", "Time.Equal"), + expr.Operator("<", "Time.Before"), + expr.Operator(">", "Time.After"), + expr.Operator("<=", "Time.BeforeOrEqual"), + expr.Operator(">", "Time.After"), + expr.Operator(">=", "Time.AfterOrEqual"), + + // Time and duration manipulation. + expr.Operator("+", "Time.Add"), + expr.Operator("-", "Time.Sub"), + + // Operators override for duration comprising. + expr.Operator("==", "Time.EqualDuration"), + expr.Operator("<", "Time.BeforeDuration"), + expr.Operator("<=", "Time.BeforeOrEqualDuration"), + expr.Operator(">", "Time.AfterDuration"), + expr.Operator(">=", "Time.AfterOrEqualDuration"), + ) +} + +func (TimeExpr) Time(v interface{}) time.Time { + switch val := v.(type) { + + case string: + if t, err := time.Parse(DefaultTimeLayout, val); err != nil { + panic(err) + } else { + return t + } + case time.Time: + return v.(time.Time) + } + return v.(time.Time) +} + +func (TimeExpr) Date(s string) time.Time { + t, err := time.Parse(DefaultDateLayout, s) + if err != nil { + panic(err) + } + return t +} +func (TimeExpr) Duration(s string) time.Duration { + d, err := time.ParseDuration(s) + if err != nil { + panic(err) + } + return d +} + +func (TimeExpr) Now() time.Time { return time.Now() } +func (TimeExpr) Equal(a, b time.Time) bool { return a.Equal(b) } +func (TimeExpr) Before(a, b time.Time) bool { return a.Before(b) } +func (TimeExpr) BeforeOrEqual(a, b time.Time) bool { return a.Before(b) || a.Equal(b) } +func (TimeExpr) After(a, b time.Time) bool { return a.After(b) } +func (TimeExpr) AfterOrEqual(a, b time.Time) bool { return a.After(b) || a.Equal(b) } +func (TimeExpr) Add(a time.Time, b time.Duration) time.Time { return a.Add(b) } +func (TimeExpr) Sub(a, b time.Time) time.Duration { return a.Sub(b) } +func (TimeExpr) EqualDuration(a, b time.Duration) bool { return a == b } +func (TimeExpr) BeforeDuration(a, b time.Duration) bool { return a < b } +func (TimeExpr) BeforeOrEqualDuration(a, b time.Duration) bool { return a <= b } +func (TimeExpr) AfterDuration(a, b time.Duration) bool { return a > b } +func (TimeExpr) AfterOrEqualDuration(a, b time.Duration) bool { return a >= b } diff --git a/pkg/id/id.go b/pkg/id/id.go new file mode 100644 index 0000000000000000000000000000000000000000..cfb8b6390ac48e50ed5fb899db11316b3f1df31c --- /dev/null +++ b/pkg/id/id.go @@ -0,0 +1,7 @@ +package id + +import "github.com/rs/xid" + +func GenerateNewID() string { + return xid.New().String() +} diff --git a/pkg/locales/locale.go b/pkg/locales/locale.go new file mode 100644 index 0000000000000000000000000000000000000000..d3cc43c6c625a5090d975409d7aa2beb16eefc3a --- /dev/null +++ b/pkg/locales/locale.go @@ -0,0 +1,7 @@ +package locales + +type Locale struct { + ID string `json:"id" bson:"_id"` // (Пример: "en", "en-US") + SpaceID string `json:"spaceId" bson:"-"` + Name string `json:"name" bson:"name"` // (Пример: "English", "English (US)" ) +} diff --git a/pkg/locales/mocks/Locales.go b/pkg/locales/mocks/Locales.go new file mode 100644 index 0000000000000000000000000000000000000000..3e63dfedfff975f52f886d19e1e8982bccb96b7e --- /dev/null +++ b/pkg/locales/mocks/Locales.go @@ -0,0 +1,75 @@ +// Code generated by mockery v2.7.4. DO NOT EDIT. + +package mocks + +import ( + context "context" + + locales "git.perx.ru/perxis/perxis-go/pkg/locales" + mock "github.com/stretchr/testify/mock" +) + +// Locales is an autogenerated mock type for the Locales type +type Locales struct { + mock.Mock +} + +// Create provides a mock function with given fields: ctx, locale +func (_m *Locales) Create(ctx context.Context, locale *locales.Locale) (*locales.Locale, error) { + ret := _m.Called(ctx, locale) + + var r0 *locales.Locale + if rf, ok := ret.Get(0).(func(context.Context, *locales.Locale) *locales.Locale); ok { + r0 = rf(ctx, locale) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*locales.Locale) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *locales.Locale) error); ok { + r1 = rf(ctx, locale) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Delete provides a mock function with given fields: ctx, spaceId, localeId +func (_m *Locales) Delete(ctx context.Context, spaceId string, localeId string) error { + ret := _m.Called(ctx, spaceId, localeId) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + r0 = rf(ctx, spaceId, localeId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// List provides a mock function with given fields: ctx, spaceId +func (_m *Locales) List(ctx context.Context, spaceId string) ([]*locales.Locale, error) { + ret := _m.Called(ctx, spaceId) + + var r0 []*locales.Locale + if rf, ok := ret.Get(0).(func(context.Context, string) []*locales.Locale); ok { + r0 = rf(ctx, spaceId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*locales.Locale) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, spaceId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/locales/service.go b/pkg/locales/service.go new file mode 100644 index 0000000000000000000000000000000000000000..7724d7f7ecdb2f4733d97bff01113d105adabdeb --- /dev/null +++ b/pkg/locales/service.go @@ -0,0 +1,14 @@ +package locales + +import ( + "context" +) + +// @microgen grpc +// @protobuf git.perx.ru/perxis/perxis-go/proto/locales +// @grpc-addr content.locales.Locales +type Locales interface { + Create(ctx context.Context, locale *Locale) (created *Locale, err error) + List(ctx context.Context, spaceId string) (locales []*Locale, err error) + Delete(ctx context.Context, spaceId, localeId string) (err error) +} diff --git a/pkg/locales/transport/client.microgen.go b/pkg/locales/transport/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..f8cd9dee23dafeff1d635b3f5ee42f6e5c8667f1 --- /dev/null +++ b/pkg/locales/transport/client.microgen.go @@ -0,0 +1,51 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + "errors" + + locales "git.perx.ru/perxis/perxis-go/pkg/locales" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +func (set EndpointsSet) Create(arg0 context.Context, arg1 *locales.Locale) (res0 *locales.Locale, res1 error) { + request := CreateRequest{Locale: arg1} + response, res1 := set.CreateEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*CreateResponse).Created, res1 +} + +func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*locales.Locale, res1 error) { + request := ListRequest{SpaceId: arg1} + response, res1 := set.ListEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*ListResponse).Locales, res1 +} + +func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (res0 error) { + request := DeleteRequest{ + LocaleId: arg2, + SpaceId: arg1, + } + _, res0 = set.DeleteEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} diff --git a/pkg/locales/transport/endpoints.microgen.go b/pkg/locales/transport/endpoints.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..ffca7318747104f8b58af332e90646f8cc6a8b9c --- /dev/null +++ b/pkg/locales/transport/endpoints.microgen.go @@ -0,0 +1,12 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import endpoint "github.com/go-kit/kit/endpoint" + +// EndpointsSet implements Locales API and used for transport purposes. +type EndpointsSet struct { + CreateEndpoint endpoint.Endpoint + ListEndpoint endpoint.Endpoint + DeleteEndpoint endpoint.Endpoint +} diff --git a/pkg/locales/transport/exchanges.microgen.go b/pkg/locales/transport/exchanges.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..a07204e13a233fd7bae40c74e9984061871a92b7 --- /dev/null +++ b/pkg/locales/transport/exchanges.microgen.go @@ -0,0 +1,28 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import locales "git.perx.ru/perxis/perxis-go/pkg/locales" + +type ( + CreateRequest struct { + Locale *locales.Locale `json:"locale"` + } + CreateResponse struct { + Created *locales.Locale `json:"created"` + } + + ListRequest struct { + SpaceId string `json:"space_id"` + } + ListResponse struct { + Locales []*locales.Locale `json:"locales"` + } + + DeleteRequest struct { + SpaceId string `json:"space_id"` + LocaleId string `json:"locale_id"` + } + // Formal exchange type, please do not delete. + DeleteResponse struct{} +) diff --git a/pkg/locales/transport/grpc/client.microgen.go b/pkg/locales/transport/grpc/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..3af5bbae0ed86bf079d93622cc8f1aa4f7fbdfd8 --- /dev/null +++ b/pkg/locales/transport/grpc/client.microgen.go @@ -0,0 +1,40 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/locales/transport" + pb "git.perx.ru/perxis/perxis-go/proto/locales" + grpckit "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" +) + +func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet { + if addr == "" { + addr = "content.locales.Locales" + } + return transport.EndpointsSet{ + CreateEndpoint: grpckit.NewClient( + conn, addr, "Create", + _Encode_Create_Request, + _Decode_Create_Response, + pb.CreateResponse{}, + opts..., + ).Endpoint(), + DeleteEndpoint: grpckit.NewClient( + conn, addr, "Delete", + _Encode_Delete_Request, + _Decode_Delete_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + ListEndpoint: grpckit.NewClient( + conn, addr, "List", + _Encode_List_Request, + _Decode_List_Response, + pb.ListResponse{}, + opts..., + ).Endpoint(), + } +} diff --git a/pkg/locales/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/locales/transport/grpc/protobuf_endpoint_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..5d06e8232c89f226e4f7c0a80ca6d52eb219d48f --- /dev/null +++ b/pkg/locales/transport/grpc/protobuf_endpoint_converters.microgen.go @@ -0,0 +1,131 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// Please, do not change functions names! +package transportgrpc + +import ( + "context" + "errors" + + transport "git.perx.ru/perxis/perxis-go/pkg/locales/transport" + pb "git.perx.ru/perxis/perxis-go/proto/locales" + empty "github.com/golang/protobuf/ptypes/empty" +) + +func _Encode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil CreateRequest") + } + req := request.(*transport.CreateRequest) + pbLocale, err := PtrLocaleToProto(req.Locale) + if err != nil { + return nil, err + } + return &pb.CreateRequest{Locale: pbLocale}, nil +} + +func _Encode_List_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListRequest") + } + req := request.(*transport.ListRequest) + return &pb.ListRequest{SpaceId: req.SpaceId}, nil +} + +func _Encode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil DeleteRequest") + } + req := request.(*transport.DeleteRequest) + return &pb.DeleteRequest{ + LocaleId: req.LocaleId, + SpaceId: req.SpaceId, + }, nil +} + +func _Encode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil CreateResponse") + } + resp := response.(*transport.CreateResponse) + respLocale, err := PtrLocaleToProto(resp.Created) + if err != nil { + return nil, err + } + return &pb.CreateResponse{Locale: respLocale}, nil +} + +func _Encode_List_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListResponse") + } + resp := response.(*transport.ListResponse) + respLocales, err := ListPtrLocaleToProto(resp.Locales) + if err != nil { + return nil, err + } + return &pb.ListResponse{Locales: respLocales}, nil +} + +func _Encode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil CreateRequest") + } + req := request.(*pb.CreateRequest) + locale, err := ProtoToPtrLocale(req.Locale) + if err != nil { + return nil, err + } + return &transport.CreateRequest{Locale: locale}, nil +} + +func _Decode_List_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil ListRequest") + } + req := request.(*pb.ListRequest) + return &transport.ListRequest{SpaceId: string(req.SpaceId)}, nil +} + +func _Decode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil DeleteRequest") + } + req := request.(*pb.DeleteRequest) + return &transport.DeleteRequest{ + LocaleId: string(req.LocaleId), + SpaceId: string(req.SpaceId), + }, nil +} + +func _Decode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil CreateResponse") + } + resp := response.(*pb.CreateResponse) + respLocale, err := ProtoToPtrLocale(resp.Locale) + if err != nil { + return nil, err + } + return &transport.CreateResponse{Created: respLocale}, nil +} + +func _Decode_List_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil ListResponse") + } + resp := response.(*pb.ListResponse) + respLocales, err := ProtoToListPtrLocale(resp.Locales) + if err != nil { + return nil, err + } + return &transport.ListResponse{Locales: respLocales}, nil +} + +func _Decode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} diff --git a/pkg/locales/transport/grpc/protobuf_type_converters.microgen.go b/pkg/locales/transport/grpc/protobuf_type_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..6dca0bb8afa68dfe3fa17b53d315f3716c48ae36 --- /dev/null +++ b/pkg/locales/transport/grpc/protobuf_type_converters.microgen.go @@ -0,0 +1,48 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// It is better for you if you do not change functions names! +// This file will never be overwritten. +package transportgrpc + +import ( + service "git.perx.ru/perxis/perxis-go/pkg/locales" + pb "git.perx.ru/perxis/perxis-go/proto/locales" +) + +func PtrLocaleToProto(locale *service.Locale) (*pb.Locale, error) { + if locale == nil { + return nil, nil + } + return &pb.Locale{Id: locale.ID, Name: locale.Name, SpaceId: locale.SpaceID}, nil +} + +func ProtoToPtrLocale(protoLocale *pb.Locale) (*service.Locale, error) { + if protoLocale == nil { + return nil, nil + } + return &service.Locale{ID: protoLocale.Id, Name: protoLocale.Name, SpaceID: protoLocale.SpaceId}, nil +} + +func ListPtrLocaleToProto(locales []*service.Locale) ([]*pb.Locale, error) { + protoLocales := make([]*pb.Locale, 0, len(locales)) + for _, l := range locales { + pl, err := PtrLocaleToProto(l) + if err != nil { + return nil, err + } + protoLocales = append(protoLocales, pl) + } + return protoLocales, nil +} + +func ProtoToListPtrLocale(protoLocales []*pb.Locale) ([]*service.Locale, error) { + locales := make([]*service.Locale, 0, len(protoLocales)) + for _, pl := range protoLocales { + l, err := ProtoToPtrLocale(pl) + if err != nil { + return nil, err + } + locales = append(locales, l) + } + return locales, nil +} diff --git a/pkg/locales/transport/grpc/server.microgen.go b/pkg/locales/transport/grpc/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..88e549f6e23e99a04d037fcefb4603a13821d24a --- /dev/null +++ b/pkg/locales/transport/grpc/server.microgen.go @@ -0,0 +1,67 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// DO NOT EDIT. +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/locales/transport" + pb "git.perx.ru/perxis/perxis-go/proto/locales" + grpc "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + context "golang.org/x/net/context" +) + +type localesServer struct { + create grpc.Handler + list grpc.Handler + delete grpc.Handler + + pb.UnimplementedLocalesServer +} + +func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.LocalesServer { + return &localesServer{ + create: grpc.NewServer( + endpoints.CreateEndpoint, + _Decode_Create_Request, + _Encode_Create_Response, + opts..., + ), + delete: grpc.NewServer( + endpoints.DeleteEndpoint, + _Decode_Delete_Request, + _Encode_Delete_Response, + opts..., + ), + list: grpc.NewServer( + endpoints.ListEndpoint, + _Decode_List_Request, + _Encode_List_Response, + opts..., + ), + } +} + +func (S *localesServer) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateResponse, error) { + _, resp, err := S.create.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.CreateResponse), nil +} + +func (S *localesServer) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) { + _, resp, err := S.list.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.ListResponse), nil +} + +func (S *localesServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*empty.Empty, error) { + _, resp, err := S.delete.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} diff --git a/pkg/locales/transport/server.microgen.go b/pkg/locales/transport/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..5ce815dcb52415a93ddfeb095f99e22fb14c4492 --- /dev/null +++ b/pkg/locales/transport/server.microgen.go @@ -0,0 +1,42 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + + locales "git.perx.ru/perxis/perxis-go/pkg/locales" + endpoint "github.com/go-kit/kit/endpoint" +) + +func Endpoints(svc locales.Locales) EndpointsSet { + return EndpointsSet{ + CreateEndpoint: CreateEndpoint(svc), + DeleteEndpoint: DeleteEndpoint(svc), + ListEndpoint: ListEndpoint(svc), + } +} + +func CreateEndpoint(svc locales.Locales) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*CreateRequest) + res0, res1 := svc.Create(arg0, req.Locale) + return &CreateResponse{Created: res0}, res1 + } +} + +func ListEndpoint(svc locales.Locales) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*ListRequest) + res0, res1 := svc.List(arg0, req.SpaceId) + return &ListResponse{Locales: res0}, res1 + } +} + +func DeleteEndpoint(svc locales.Locales) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*DeleteRequest) + res0 := svc.Delete(arg0, req.SpaceId, req.LocaleId) + return &DeleteResponse{}, res0 + } +} diff --git a/pkg/options/options.go b/pkg/options/options.go new file mode 100644 index 0000000000000000000000000000000000000000..7f8b0cd624db7e6c4c35fe62333a67ba633fbe7e --- /dev/null +++ b/pkg/options/options.go @@ -0,0 +1,122 @@ +package options + +import "time" + +// SortOptions наÑтройки Ñортировки результатов +type SortOptions struct { + Sort []string +} + +// PaginationOptions наÑтройки возвращаемых Ñтраниц результатов +type PaginationOptions struct { + PageNum int + PageSize int +} + +// FieldOptions наÑтройки включениÑ/иÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÐµÐ¹ из результатов запроÑа +type FieldOptions struct { + // Fields - ÐÐ°Ð¸Ð¼ÐµÐ½Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾Ð»ÐµÐ¹ Ð´Ð»Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ/иÑÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð· результатов запроÑа (только указанные полÑ) + // ЕÑли `ExcludeFields` не уÑтановлен, то результат Ñодержит только указанные Ð¿Ð¾Ð»Ñ + // ЕÑли `ExcludeFields` уÑтановлен, то результат Ñодержит вÑе Ð¿Ð¾Ð»Ñ ÐºÑ€Ð¾Ð¼Ðµ указанных + Fields []string + + // ExcludeFields- ЕÑли флаг уÑтановлен, то перечиÑленные Ð¿Ð¾Ð»Ñ `Fields` Ñледует иÑключить из результатов + ExcludeFields bool +} + +// FindOptions наÑтройки возвращаемых результатов поиÑка +type FindOptions struct { + SortOptions + PaginationOptions + FieldOptions +} + +// NewFindOptions Ñоздает новые результаты поиÑка +func NewFindOptions(pageNum, pageSize int, sort ...string) *FindOptions { + return &FindOptions{ + PaginationOptions: PaginationOptions{ + PageNum: pageNum, + PageSize: pageSize, + }, + SortOptions: SortOptions{ + Sort: sort, + }, + } +} + +// MergeFindOptions объединÑет в FindOptions различные варианты наÑтроек +func MergeFindOptions(opts ...interface{}) *FindOptions { + fo := &FindOptions{} + for _, opt := range opts { + if opt == nil { + continue + } + + switch o := opt.(type) { + case FindOptions: + fo.SortOptions = MergeSortOptions(fo.SortOptions, o.SortOptions) + fo.PaginationOptions = MergePaginationOptions(fo.PaginationOptions, o.PaginationOptions) + fo.FieldOptions = MergeFieldOptions(fo.FieldOptions, o.FieldOptions) + case *FindOptions: + fo.SortOptions = MergeSortOptions(fo.SortOptions, o.SortOptions) + fo.PaginationOptions = MergePaginationOptions(fo.PaginationOptions, o.PaginationOptions) + fo.FieldOptions = MergeFieldOptions(fo.FieldOptions, o.FieldOptions) + case SortOptions: + fo.SortOptions = MergeSortOptions(fo.SortOptions, o) + case *SortOptions: + fo.SortOptions = MergeSortOptions(fo.SortOptions, *o) + case PaginationOptions: + fo.PaginationOptions = MergePaginationOptions(fo.PaginationOptions, o) + case *PaginationOptions: + fo.PaginationOptions = MergePaginationOptions(fo.PaginationOptions, *o) + case FieldOptions: + fo.FieldOptions = o + case *FieldOptions: + fo.FieldOptions = *o + } + } + return fo +} + +type TimeFilter struct { + Before, After time.Time +} + +// MergeSortOptions объединÑет наÑтройки Ñортировки +func MergeSortOptions(options ...SortOptions) SortOptions { + fo := SortOptions{} + for _, opt := range options { + if len(opt.Sort) == 0 { + continue + } + fo.Sort = append(fo.Sort, opt.Sort...) + } + return fo +} + +// MergePaginationOptions объединÑет наÑтройки Ñтраниц +func MergePaginationOptions(options ...PaginationOptions) PaginationOptions { + fo := PaginationOptions{} + for _, opt := range options { + if opt.PageSize == 0 && opt.PageNum == 0 { + continue + } + fo.PageNum = opt.PageNum + fo.PageSize = opt.PageSize + } + return fo +} + +// MergeFieldOptions выполнÑет ÑлиÑние опций Ð´Ð»Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÐ¼Ñ‹Ñ… полей. +// ВыбираетÑÑ Ð½Ðµ пуÑтое значение. +func MergeFieldOptions(options ...FieldOptions) FieldOptions { + fo := FieldOptions{} + for _, opt := range options { + if len(opt.Fields) > 0 { + fo.Fields = opt.Fields + fo.ExcludeFields = opt.ExcludeFields + return fo + } + } + return fo +} diff --git a/pkg/options/options_test.go b/pkg/options/options_test.go new file mode 100644 index 0000000000000000000000000000000000000000..981849a0e2625a8bd7e796a22eaa8529d606ef01 --- /dev/null +++ b/pkg/options/options_test.go @@ -0,0 +1,60 @@ +package options + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOptions_MergePaginationOptions(t *testing.T) { + + var tt = []struct { + name string + options []PaginationOptions + expected PaginationOptions + }{ + { + name: "Nil option", + options: nil, + expected: PaginationOptions{}, + }, + { + name: "Empty options", + options: []PaginationOptions{}, + expected: PaginationOptions{}, + }, + { + name: "One option", + options: []PaginationOptions{{PageNum: 10, PageSize: 100}}, + expected: PaginationOptions{PageNum: 10, PageSize: 100}, + }, + { + name: "Merge #1", + options: []PaginationOptions{{PageNum: 0, PageSize: 0}, {PageNum: 10, PageSize: 100}}, + expected: PaginationOptions{PageNum: 10, PageSize: 100}, + }, + { + name: "Merge #2", + options: []PaginationOptions{{PageNum: 10, PageSize: 100}, {PageNum: 0, PageSize: 0}}, + expected: PaginationOptions{PageNum: 10, PageSize: 100}, + }, + { + name: "Merge #3", + options: []PaginationOptions{{PageNum: 0, PageSize: 0}, {PageNum: 10, PageSize: 100}, {PageNum: 0, PageSize: 0}}, + expected: PaginationOptions{PageNum: 10, PageSize: 100}, + }, + { + name: "Merge #4", + options: []PaginationOptions{{PageNum: 10, PageSize: 100}, {}}, + expected: PaginationOptions{PageNum: 10, PageSize: 100}, + }, + } + + for _, v := range tt { + + t.Run(v.name, func(t *testing.T) { + actual := MergePaginationOptions(v.options...) + assert.Equal(t, v.expected, actual) + }) + } +} diff --git a/pkg/permission/permission.go b/pkg/permission/permission.go new file mode 100644 index 0000000000000000000000000000000000000000..7a90e5079da08648cbdefe565d39f4d8b801a275 --- /dev/null +++ b/pkg/permission/permission.go @@ -0,0 +1,64 @@ +package permission + +import ( + "git.perx.ru/perxis/perxis-go/pkg/data" + "git.perx.ru/perxis/perxis-go/pkg/expr" +) + +type Access uint64 + +const ( + AccessAny = iota // Access to any items (default) + AccessMine // Access to items owned by me (future use) + AccessRole // Access to items owned by my role (future use) +) + +type Permission struct { + Permitted bool + UnallowedFields []string + Filter string +} + +func (p Permission) Can() bool { + return p.Permitted +} + +func Merge(pp ...*Permission) *Permission { + + res := new(Permission) + + var filters []string + + res.Permitted = true + for _, p := range pp { + res.Permitted = res.Permitted && p.Permitted + + if p.Filter != "" { + filters = append(filters, p.Filter) + } + + for _, fld := range p.UnallowedFields { + if !data.Contains(fld, res.UnallowedFields) { + res.UnallowedFields = append(res.UnallowedFields, fld) + } + } + } + + res.Filter = expr.And(data.SetFromSlice(filters)...) + + return res +} + +func (p Permission) RemoveFields(in map[string]interface{}) map[string]interface{} { + if in == nil { + return nil + } + out := make(map[string]interface{}) + for k, v := range in { + if data.Contains(k, p.UnallowedFields) { + continue + } + out[k] = v + } + return out +} diff --git a/pkg/permission/ruleset.go b/pkg/permission/ruleset.go new file mode 100644 index 0000000000000000000000000000000000000000..b9a84f3747223cf4139c905e3d65ebb9f4dea336 --- /dev/null +++ b/pkg/permission/ruleset.go @@ -0,0 +1,266 @@ +package permission + +import ( + "git.perx.ru/perxis/perxis-go/pkg/data" + perxisexpr "git.perx.ru/perxis/perxis-go/pkg/expr" +) + +type Action uint64 + +const ( + //ActionAny Action = iota + ActionCreate Action = iota + 1 + ActionRead + ActionUpdate + ActionDelete +) + +// Rule - правило доÑтупа к контенту +type Rule struct { + CollectionID string `json:"collectionId" bson:"collectionId,omitempty"` + Actions []Action `json:"actions" bson:"actions,omitempty"` + Access Access `json:"access" bson:"access,omitempty"` + // ÐŸÐ¾Ð»Ñ Ð½Ðµ передаютÑÑ API клиенту + HiddenFields []string `json:"hiddenFields,omitempty" bson:"hiddenFields,omitempty"` + // Клиент не может ÑохранÑть данные Ð¿Ð¾Ð»Ñ + ReadonlyFields []string `json:"readonlyFields,omitempty" bson:"readonlyFields,omitempty"` + // Клиент может Ñохранить данные полÑ, но Ð¿Ð¾Ð»Ñ Ð½Ðµ передаютÑÑ Ð² API + WriteonlyFields []string `json:"writeonlyFields,omitempty" bson:"writeonlyFields,omitempty"` + // Дополнительный фильтр + ReadFilter string `json:"readFilter,omitempty" bson:"readFilter,omitempty"` + WriteFilter string `json:"writeFilter,omitempty" bson:"writeFilter,omitempty"` +} + +func NewRule(collectionID string, actions ...Action) *Rule { + return &Rule{ + CollectionID: collectionID, + Actions: actions, + } +} + +func (r Rule) Clone() *Rule { + return &Rule{ + CollectionID: r.CollectionID, + Actions: append([]Action(nil), r.Actions...), + Access: r.Access, + HiddenFields: append([]string(nil), r.HiddenFields...), + ReadonlyFields: append([]string(nil), r.ReadonlyFields...), + WriteonlyFields: append([]string(nil), r.WriteonlyFields...), + ReadFilter: r.ReadFilter, + WriteFilter: r.WriteFilter, + } +} + +func (r Rule) WithReadFilter(f string) *Rule { + return &Rule{ + CollectionID: r.CollectionID, + Actions: append([]Action(nil), r.Actions...), + Access: r.Access, + HiddenFields: append([]string(nil), r.HiddenFields...), + ReadonlyFields: append([]string(nil), r.ReadonlyFields...), + WriteonlyFields: append([]string(nil), r.WriteonlyFields...), + ReadFilter: f, + WriteFilter: r.WriteFilter, + } +} + +func (r Rule) WithWriteFilter(f string) *Rule { + return &Rule{ + CollectionID: r.CollectionID, + Actions: append([]Action(nil), r.Actions...), + Access: r.Access, + HiddenFields: append([]string(nil), r.HiddenFields...), + ReadonlyFields: append([]string(nil), r.ReadonlyFields...), + WriteonlyFields: append([]string(nil), r.WriteonlyFields...), + ReadFilter: r.ReadFilter, + WriteFilter: f, + } +} + +func (r Rule) WithReadWriteFilter(f string) *Rule { + return &Rule{ + CollectionID: r.CollectionID, + Actions: append([]Action(nil), r.Actions...), + Access: r.Access, + HiddenFields: append([]string(nil), r.HiddenFields...), + ReadonlyFields: append([]string(nil), r.ReadonlyFields...), + WriteonlyFields: append([]string(nil), r.WriteonlyFields...), + ReadFilter: f, + WriteFilter: f, + } +} + +func (r Rule) WithReadonlyFields(ff ...string) *Rule { + return &Rule{ + CollectionID: r.CollectionID, + Actions: append([]Action(nil), r.Actions...), + Access: r.Access, + HiddenFields: append([]string(nil), r.HiddenFields...), + ReadonlyFields: append(ff, r.ReadonlyFields...), + WriteonlyFields: append([]string(nil), r.WriteonlyFields...), + ReadFilter: r.ReadFilter, + WriteFilter: r.WriteFilter, + } +} + +func (r Rule) WithHiddenFields(ff ...string) *Rule { + return &Rule{ + CollectionID: r.CollectionID, + Actions: append([]Action(nil), r.Actions...), + Access: r.Access, + HiddenFields: append(ff, r.HiddenFields...), + ReadonlyFields: append([]string(nil), r.ReadonlyFields...), + WriteonlyFields: append([]string(nil), r.WriteonlyFields...), + ReadFilter: r.ReadFilter, + WriteFilter: r.WriteFilter, + } +} + +func (r Rule) GetPermission(action Action) *Permission { + for _, a := range r.Actions { + if a == action { + p := &Permission{ + Permitted: true, + } + + switch action { + case ActionRead: + p.Filter = r.ReadFilter + p.UnallowedFields = append(p.UnallowedFields, r.HiddenFields...) + p.UnallowedFields = append(p.UnallowedFields, r.WriteonlyFields...) + case ActionCreate, ActionUpdate, ActionDelete: + p.Filter = r.WriteFilter + p.UnallowedFields = append(p.UnallowedFields, r.ReadonlyFields...) + } + + return p + } + } + + return &Permission{} +} + +type Ruleset interface { + GetRule(collectionID string) *Rule + Permission(collectionID string, action Action) *Permission +} + +type Rules []*Rule + +func (r Rules) Permission(collectionID string, action Action) *Permission { + rule := r.GetRule(collectionID) + return rule.GetPermission(action) +} + +func (r Rules) GetRule(collectionID string) *Rule { + for _, rule := range r { + if data.GlobMatch(collectionID, rule.CollectionID) { + return rule + } + } + return nil +} + +func MergeRules(src, in Rules) Rules { + dst := make(Rules, 0, len(src)+len(in)) + seen := make(map[string]struct{}) + + for _, rule := range src { + if _, ok := seen[rule.CollectionID]; !ok { + dst = append(dst, rule) + seen[rule.CollectionID] = struct{}{} + } + } + + for _, rule := range in { + if _, ok := seen[rule.CollectionID]; !ok { + dst = append(dst, rule) + seen[rule.CollectionID] = struct{}{} + } + } + + return dst +} + +// MergeRule объединÑет неÑколько Rule в один +// - переÑечение дейÑтвий +// - объединение hidden, readOnly, writeOnly fields +// - объединение фильтров +func MergeRule(rules ...*Rule) *Rule { + + if len(rules) == 0 { + return nil + } + + var result *Rule + var writeFilter []string + var readFilter []string + + for i, r := range rules { + if i == 0 { // first element + + result = r.Clone() + + result.CollectionID = "" + if result.WriteFilter != "" { + writeFilter = append(writeFilter, result.WriteFilter) + } + if result.ReadFilter != "" { + readFilter = append(readFilter, result.ReadFilter) + } + + continue + } + + result.Actions = data.GetIntersection(result.Actions, r.Actions) + result.HiddenFields = data.SetFromSlice(append(result.HiddenFields, r.HiddenFields...)) + result.ReadonlyFields = data.SetFromSlice(append(result.ReadonlyFields, r.ReadonlyFields...)) + result.WriteonlyFields = data.SetFromSlice(append(result.WriteonlyFields, r.WriteonlyFields...)) + if r.WriteFilter != "" { + writeFilter = append(writeFilter, r.WriteFilter) + } + if r.ReadFilter != "" { + readFilter = append(readFilter, r.ReadFilter) + } + } + + result.WriteFilter = perxisexpr.And(data.SetFromSlice(writeFilter)...) + result.ReadFilter = perxisexpr.And(data.SetFromSlice(readFilter)...) + + return result +} + +type PrivilegedRuleset struct{} + +func (r PrivilegedRuleset) Permission(_ string, _ Action) *Permission { + return &Permission{ + Permitted: true, + UnallowedFields: []string{}, + } +} + +func (r PrivilegedRuleset) GetRule(collectionID string) *Rule { + return &Rule{ + CollectionID: collectionID, + Actions: []Action{ActionRead, ActionCreate, ActionUpdate, ActionDelete}, + HiddenFields: []string{}, + ReadonlyFields: []string{}, + WriteonlyFields: []string{}, + } +} + +func Create(r Ruleset, collectionID string) *Permission { + return r.Permission(collectionID, ActionCreate) +} + +func Read(r Ruleset, collectionID string) *Permission { + return r.Permission(collectionID, ActionRead) +} + +func Update(r Ruleset, collectionID string) *Permission { + return r.Permission(collectionID, ActionUpdate) +} + +func Delete(r Ruleset, collectionID string) *Permission { + return r.Permission(collectionID, ActionDelete) +} diff --git a/pkg/permission/ruleset_test.go b/pkg/permission/ruleset_test.go new file mode 100644 index 0000000000000000000000000000000000000000..47c1f07614b1d2edef196db6238adaf40b33e073 --- /dev/null +++ b/pkg/permission/ruleset_test.go @@ -0,0 +1,51 @@ +package permission + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMerge(t *testing.T) { + const ( + col1 = "colID" + col2 = "colViewID" + ) + + tests := []struct { + name string + first, second, expect *Rule + }{ + { + name: "simple", + first: &Rule{Actions: []Action{ActionUpdate, ActionCreate}, CollectionID: col1, HiddenFields: []string{"1", "2"}, WriteonlyFields: []string{"7"}, ReadonlyFields: []string{"4"}, ReadFilter: "3 != 'test'", WriteFilter: "4 == '0_0'"}, + second: &Rule{Actions: []Action{ActionUpdate, ActionDelete}, CollectionID: col2, HiddenFields: []string{"3", "2"}, WriteonlyFields: []string{}, ReadonlyFields: []string{"5"}, ReadFilter: "5 != 'dev'", WriteFilter: "4 == '0_0'"}, + expect: &Rule{Actions: []Action{ActionUpdate}, HiddenFields: []string{"1", "2", "3"}, WriteonlyFields: []string{"7"}, ReadonlyFields: []string{"4", "5"}, ReadFilter: "3 != 'test' && 5 != 'dev'", WriteFilter: "4 == '0_0'"}, + }, + { + name: "first is privileged", + first: PrivilegedRuleset{}.GetRule(col1), + second: &Rule{Actions: []Action{ActionUpdate, ActionDelete}, CollectionID: col2, WriteFilter: "test"}, + expect: &Rule{Actions: []Action{ActionUpdate, ActionDelete}, WriteFilter: "test", ReadFilter: "", HiddenFields: []string{}, WriteonlyFields: []string{}, ReadonlyFields: []string{}}, + }, + { + name: "second is privileged", + first: &Rule{Actions: []Action{ActionUpdate, ActionDelete}, CollectionID: col1, WriteFilter: "test"}, + second: PrivilegedRuleset{}.GetRule(col2), + expect: &Rule{Actions: []Action{ActionUpdate, ActionDelete}, WriteFilter: "test", ReadFilter: "", HiddenFields: []string{}, WriteonlyFields: []string{}, ReadonlyFields: []string{}}, + }, + { + name: "both is privileged", + first: PrivilegedRuleset{}.GetRule(col1), + second: PrivilegedRuleset{}.GetRule(col2), + expect: PrivilegedRuleset{}.GetRule(""), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := MergeRule(tt.first, tt.second) + assert.Equal(t, tt.expect, result) + }) + } +} diff --git a/pkg/space/mocks/Spaces.go b/pkg/spaces/mocks/Spaces.go similarity index 70% rename from pkg/space/mocks/Spaces.go rename to pkg/spaces/mocks/Spaces.go index 6fd9ef4aaf8012c82973ed3d94815babed9acd65..e272ebceb87c26a672a3aaf5f7dde3dd4858991e 100644 --- a/pkg/space/mocks/Spaces.go +++ b/pkg/spaces/mocks/Spaces.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - space "git.perx.ru/perxis/perxis-go/pkg/space" + spaces "git.perx.ru/perxis/perxis-go/pkg/spaces" mock "github.com/stretchr/testify/mock" ) @@ -15,23 +15,23 @@ type Spaces struct { } // Create provides a mock function with given fields: ctx, _a1 -func (_m *Spaces) Create(ctx context.Context, _a1 *space.Space) (*space.Space, error) { +func (_m *Spaces) Create(ctx context.Context, _a1 *spaces.Space) (*spaces.Space, error) { ret := _m.Called(ctx, _a1) - var r0 *space.Space + var r0 *spaces.Space var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *space.Space) (*space.Space, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) (*spaces.Space, error)); ok { return rf(ctx, _a1) } - if rf, ok := ret.Get(0).(func(context.Context, *space.Space) *space.Space); ok { + if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) *spaces.Space); ok { r0 = rf(ctx, _a1) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*space.Space) + r0 = ret.Get(0).(*spaces.Space) } } - if rf, ok := ret.Get(1).(func(context.Context, *space.Space) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *spaces.Space) error); ok { r1 = rf(ctx, _a1) } else { r1 = ret.Error(1) @@ -55,19 +55,19 @@ func (_m *Spaces) Delete(ctx context.Context, spaceId string) error { } // Get provides a mock function with given fields: ctx, spaceId -func (_m *Spaces) Get(ctx context.Context, spaceId string) (*space.Space, error) { +func (_m *Spaces) Get(ctx context.Context, spaceId string) (*spaces.Space, error) { ret := _m.Called(ctx, spaceId) - var r0 *space.Space + var r0 *spaces.Space var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) (*space.Space, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) (*spaces.Space, error)); ok { return rf(ctx, spaceId) } - if rf, ok := ret.Get(0).(func(context.Context, string) *space.Space); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) *spaces.Space); ok { r0 = rf(ctx, spaceId) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*space.Space) + r0 = ret.Get(0).(*spaces.Space) } } @@ -81,19 +81,19 @@ func (_m *Spaces) Get(ctx context.Context, spaceId string) (*space.Space, error) } // List provides a mock function with given fields: ctx, orgId -func (_m *Spaces) List(ctx context.Context, orgId string) ([]*space.Space, error) { +func (_m *Spaces) List(ctx context.Context, orgId string) ([]*spaces.Space, error) { ret := _m.Called(ctx, orgId) - var r0 []*space.Space + var r0 []*spaces.Space var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]*space.Space, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) ([]*spaces.Space, error)); ok { return rf(ctx, orgId) } - if rf, ok := ret.Get(0).(func(context.Context, string) []*space.Space); ok { + if rf, ok := ret.Get(0).(func(context.Context, string) []*spaces.Space); ok { r0 = rf(ctx, orgId) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]*space.Space) + r0 = ret.Get(0).([]*spaces.Space) } } @@ -107,11 +107,11 @@ func (_m *Spaces) List(ctx context.Context, orgId string) ([]*space.Space, error } // Update provides a mock function with given fields: ctx, _a1 -func (_m *Spaces) Update(ctx context.Context, _a1 *space.Space) error { +func (_m *Spaces) Update(ctx context.Context, _a1 *spaces.Space) error { ret := _m.Called(ctx, _a1) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *space.Space) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) error); ok { r0 = rf(ctx, _a1) } else { r0 = ret.Error(0) @@ -121,11 +121,11 @@ func (_m *Spaces) Update(ctx context.Context, _a1 *space.Space) error { } // UpdateConfig provides a mock function with given fields: ctx, spaceId, config -func (_m *Spaces) UpdateConfig(ctx context.Context, spaceId string, config *space.Config) error { +func (_m *Spaces) UpdateConfig(ctx context.Context, spaceId string, config *spaces.Config) error { ret := _m.Called(ctx, spaceId, config) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, *space.Config) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, string, *spaces.Config) error); ok { r0 = rf(ctx, spaceId, config) } else { r0 = ret.Error(0) diff --git a/pkg/space/service.go b/pkg/spaces/service.go similarity index 99% rename from pkg/space/service.go rename to pkg/spaces/service.go index 576f5c2f8a7fae3a2b15d658eb1f42b5df0cab51..d31cbfebec963d6b48ee618bfc7af85236b6aed3 100644 --- a/pkg/space/service.go +++ b/pkg/spaces/service.go @@ -1,4 +1,4 @@ -package space +package spaces import ( "context" diff --git a/pkg/space/space.go b/pkg/spaces/space.go similarity index 99% rename from pkg/space/space.go rename to pkg/spaces/space.go index dad51255a6fa9e8e648eec346a92f45c1749c876..772caa9cb248d555551de5096eb75c6ebd5e66a4 100644 --- a/pkg/space/space.go +++ b/pkg/spaces/space.go @@ -1,4 +1,4 @@ -package space +package spaces type State int diff --git a/pkg/space/transport/client.go b/pkg/spaces/transport/client.go similarity index 97% rename from pkg/space/transport/client.go rename to pkg/spaces/transport/client.go index 44b72b6f5c8f0786f7ad7a255b9e46b69f672647..4f13271e628dba08e0806e462dfed21e0f1bb7ec 100644 --- a/pkg/space/transport/client.go +++ b/pkg/spaces/transport/client.go @@ -5,7 +5,7 @@ package transport import ( "context" - spaces "git.perx.ru/perxis/perxis-go/pkg/space" + spaces "git.perx.ru/perxis/perxis-go/pkg/spaces" ) func (set EndpointsSet) Create(arg0 context.Context, arg1 *spaces.Space) (res0 *spaces.Space, res1 error) { diff --git a/pkg/space/transport/endpoints.microgen.go b/pkg/spaces/transport/endpoints.microgen.go similarity index 100% rename from pkg/space/transport/endpoints.microgen.go rename to pkg/spaces/transport/endpoints.microgen.go diff --git a/pkg/space/transport/exchanges.microgen.go b/pkg/spaces/transport/exchanges.microgen.go similarity index 94% rename from pkg/space/transport/exchanges.microgen.go rename to pkg/spaces/transport/exchanges.microgen.go index 05ad11827f34070ebd644b44a217bcceedfecbc7..106753c63bd80f38c68048767ef83c00fe19a8a4 100644 --- a/pkg/space/transport/exchanges.microgen.go +++ b/pkg/spaces/transport/exchanges.microgen.go @@ -2,7 +2,7 @@ package transport -import spaces "git.perx.ru/perxis/perxis-go/pkg/space" +import spaces "git.perx.ru/perxis/perxis-go/pkg/spaces" type ( CreateRequest struct { diff --git a/pkg/space/transport/grpc/client.go b/pkg/spaces/transport/grpc/client.go similarity index 92% rename from pkg/space/transport/grpc/client.go rename to pkg/spaces/transport/grpc/client.go index bfa48ae9df7c4bff443f5bbaa3d88c37f1fa26bc..693011ce92883ad758d1764ec6b2b6e34bc1782b 100644 --- a/pkg/space/transport/grpc/client.go +++ b/pkg/spaces/transport/grpc/client.go @@ -4,7 +4,7 @@ package transportgrpc import ( grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc" - transport "git.perx.ru/perxis/perxis-go/pkg/space/transport" + transport "git.perx.ru/perxis/perxis-go/pkg/spaces/transport" grpckit "github.com/go-kit/kit/transport/grpc" grpc "google.golang.org/grpc" ) diff --git a/pkg/space/transport/grpc/client.microgen.go b/pkg/spaces/transport/grpc/client.microgen.go similarity index 95% rename from pkg/space/transport/grpc/client.microgen.go rename to pkg/spaces/transport/grpc/client.microgen.go index b0d7d35e8e14c5ffa1d852d16e899160af5bb55e..d6547f84c58f89e44f84e2e92aa325ca349a8919 100644 --- a/pkg/space/transport/grpc/client.microgen.go +++ b/pkg/spaces/transport/grpc/client.microgen.go @@ -3,7 +3,7 @@ package transportgrpc import ( - transport "git.perx.ru/perxis/perxis-go/pkg/space/transport" + transport "git.perx.ru/perxis/perxis-go/pkg/spaces/transport" pb "git.perx.ru/perxis/perxis-go/proto/spaces" grpckit "github.com/go-kit/kit/transport/grpc" empty "github.com/golang/protobuf/ptypes/empty" diff --git a/pkg/space/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/spaces/transport/grpc/protobuf_endpoint_converters.microgen.go similarity index 99% rename from pkg/space/transport/grpc/protobuf_endpoint_converters.microgen.go rename to pkg/spaces/transport/grpc/protobuf_endpoint_converters.microgen.go index 606d41cfa3335674b487cd861fd5cfb86152b90b..3ea51a2a40ae3b6fbe2a8ef4be0fbae4cf0620f3 100644 --- a/pkg/space/transport/grpc/protobuf_endpoint_converters.microgen.go +++ b/pkg/spaces/transport/grpc/protobuf_endpoint_converters.microgen.go @@ -7,7 +7,7 @@ import ( "context" "errors" - transport "git.perx.ru/perxis/perxis-go/pkg/space/transport" + transport "git.perx.ru/perxis/perxis-go/pkg/spaces/transport" pb "git.perx.ru/perxis/perxis-go/proto/spaces" empty "github.com/golang/protobuf/ptypes/empty" ) diff --git a/pkg/space/transport/grpc/protobuf_type_converters.microgen.go b/pkg/spaces/transport/grpc/protobuf_type_converters.microgen.go similarity index 97% rename from pkg/space/transport/grpc/protobuf_type_converters.microgen.go rename to pkg/spaces/transport/grpc/protobuf_type_converters.microgen.go index 56e2dc38265de3a9fa239b3d4d8cb37873558796..27bb510db48d5d8ac09ad6101989a2395a47a32a 100644 --- a/pkg/space/transport/grpc/protobuf_type_converters.microgen.go +++ b/pkg/spaces/transport/grpc/protobuf_type_converters.microgen.go @@ -5,7 +5,7 @@ package transportgrpc import ( - service "git.perx.ru/perxis/perxis-go/pkg/space" + service "git.perx.ru/perxis/perxis-go/pkg/spaces" pb "git.perx.ru/perxis/perxis-go/proto/spaces" ) diff --git a/pkg/space/transport/grpc/server.go b/pkg/spaces/transport/grpc/server.go similarity index 80% rename from pkg/space/transport/grpc/server.go rename to pkg/spaces/transport/grpc/server.go index f8220070b19fe5cc9a51ed7bdd8aa6fe5579d428..bd38b0b922277ec39816503413faa840535d6617 100644 --- a/pkg/space/transport/grpc/server.go +++ b/pkg/spaces/transport/grpc/server.go @@ -2,13 +2,13 @@ package transportgrpc import ( grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc" - "git.perx.ru/perxis/perxis-go/pkg/space" - "git.perx.ru/perxis/perxis-go/pkg/space/transport" + "git.perx.ru/perxis/perxis-go/pkg/spaces" + "git.perx.ru/perxis/perxis-go/pkg/spaces/transport" pb "git.perx.ru/perxis/perxis-go/proto/spaces" grpckit "github.com/go-kit/kit/transport/grpc" ) -func NewServer(svc space.Spaces, opts ...grpckit.ServerOption) pb.SpacesServer { +func NewServer(svc spaces.Spaces, opts ...grpckit.ServerOption) pb.SpacesServer { eps := transport.Endpoints(svc) eps = transport.EndpointsSet{ CreateEndpoint: grpcerr.ServerMiddleware(eps.CreateEndpoint), diff --git a/pkg/space/transport/grpc/server.microgen.go b/pkg/spaces/transport/grpc/server.microgen.go similarity index 97% rename from pkg/space/transport/grpc/server.microgen.go rename to pkg/spaces/transport/grpc/server.microgen.go index 0d00433457f573073eeeabe5e41c4a3803d69c20..437b8ad385dfcb8c912c356d2ac41506ea49de79 100644 --- a/pkg/space/transport/grpc/server.microgen.go +++ b/pkg/spaces/transport/grpc/server.microgen.go @@ -4,7 +4,7 @@ package transportgrpc import ( - transport "git.perx.ru/perxis/perxis-go/pkg/space/transport" + transport "git.perx.ru/perxis/perxis-go/pkg/spaces/transport" pb "git.perx.ru/perxis/perxis-go/proto/spaces" grpc "github.com/go-kit/kit/transport/grpc" empty "github.com/golang/protobuf/ptypes/empty" diff --git a/pkg/space/transport/server.microgen.go b/pkg/spaces/transport/server.microgen.go similarity index 97% rename from pkg/space/transport/server.microgen.go rename to pkg/spaces/transport/server.microgen.go index b4546383c62dae4fdc994a79f17ba3ee4345ceb1..864dbffe26d8422848f44c6f27b513ae5033a521 100644 --- a/pkg/space/transport/server.microgen.go +++ b/pkg/spaces/transport/server.microgen.go @@ -5,7 +5,7 @@ package transport import ( "context" - spaces "git.perx.ru/perxis/perxis-go/pkg/space" + spaces "git.perx.ru/perxis/perxis-go/pkg/spaces" endpoint "github.com/go-kit/kit/endpoint" ) diff --git a/pkg/users/mocks/Users.go b/pkg/users/mocks/Users.go new file mode 100644 index 0000000000000000000000000000000000000000..6e54f18c4dfc78d8473e5499262eb7d65c783e4d --- /dev/null +++ b/pkg/users/mocks/Users.go @@ -0,0 +1,143 @@ +// Code generated by mockery v2.7.4. DO NOT EDIT. + +package mocks + +import ( + context "context" + + options "git.perx.ru/perxis/perxis-go/pkg/options" + users "git.perx.ru/perxis/perxis-go/pkg/users" + mock "github.com/stretchr/testify/mock" +) + +// Users is an autogenerated mock type for the Users type +type Users struct { + mock.Mock +} + +// Create provides a mock function with given fields: ctx, create +func (_m *Users) Create(ctx context.Context, create *users.User) (*users.User, error) { + ret := _m.Called(ctx, create) + + var r0 *users.User + if rf, ok := ret.Get(0).(func(context.Context, *users.User) *users.User); ok { + r0 = rf(ctx, create) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*users.User) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *users.User) error); ok { + r1 = rf(ctx, create) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Delete provides a mock function with given fields: ctx, userId +func (_m *Users) Delete(ctx context.Context, userId string) error { + ret := _m.Called(ctx, userId) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, userId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Find provides a mock function with given fields: ctx, filter, options +func (_m *Users) Find(ctx context.Context, filter *users.Filter, opts *options.FindOptions) ([]*users.User, int, error) { + ret := _m.Called(ctx, filter, opts) + + var r0 []*users.User + if rf, ok := ret.Get(0).(func(context.Context, *users.Filter, *options.FindOptions) []*users.User); ok { + r0 = rf(ctx, filter, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*users.User) + } + } + + var r1 int + if rf, ok := ret.Get(1).(func(context.Context, *users.Filter, *options.FindOptions) int); ok { + r1 = rf(ctx, filter, opts) + } else { + r1 = ret.Get(1).(int) + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, *users.Filter, *options.FindOptions) error); ok { + r2 = rf(ctx, filter, opts) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// Get provides a mock function with given fields: ctx, userId +func (_m *Users) Get(ctx context.Context, userId string) (*users.User, error) { + ret := _m.Called(ctx, userId) + + var r0 *users.User + if rf, ok := ret.Get(0).(func(context.Context, string) *users.User); ok { + r0 = rf(ctx, userId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*users.User) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, userId) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetByIdentity provides a mock function with given fields: ctx, identity +func (_m *Users) GetByIdentity(ctx context.Context, identity string) (*users.User, error) { + ret := _m.Called(ctx, identity) + + var r0 *users.User + if rf, ok := ret.Get(0).(func(context.Context, string) *users.User); ok { + r0 = rf(ctx, identity) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*users.User) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, identity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Update provides a mock function with given fields: ctx, update +func (_m *Users) Update(ctx context.Context, update *users.User) error { + ret := _m.Called(ctx, update) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *users.User) error); ok { + r0 = rf(ctx, update) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/pkg/users/service.go b/pkg/users/service.go new file mode 100644 index 0000000000000000000000000000000000000000..d64a5ceceed51d3731eaa6641baf64409cc7389d --- /dev/null +++ b/pkg/users/service.go @@ -0,0 +1,30 @@ +package users + +import ( + "context" + + "git.perx.ru/perxis/perxis-go/pkg/options" +) + +// @microgen grpc +// @protobuf git.perx.ru/perxis/perxis-go/proto/users +// @grpc-addr account.users.Users +type Users interface { + Create(ctx context.Context, create *User) (user *User, err error) + Get(ctx context.Context, userId string) (user *User, err error) + Find(ctx context.Context, filter *Filter, options *options.FindOptions) (users []*User, total int, err error) + Update(ctx context.Context, update *User) (err error) + Delete(ctx context.Context, userId string) (err error) + GetByIdentity(ctx context.Context, identity string) (user *User, err error) +} + +type Filter struct { + ID []string + Name []string + Identities []string + DisplayName []string + Email []string + AvatarUri []string + EmailVerified *bool + System *bool +} diff --git a/pkg/users/transport/client.microgen.go b/pkg/users/transport/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..74ca261a3bc5ec1cf99c655c82ec5b0345489ce3 --- /dev/null +++ b/pkg/users/transport/client.microgen.go @@ -0,0 +1,88 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + "errors" + + options "git.perx.ru/perxis/perxis-go/pkg/options" + users "git.perx.ru/perxis/perxis-go/pkg/users" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +func (set EndpointsSet) Create(arg0 context.Context, arg1 *users.User) (res0 *users.User, res1 error) { + request := CreateRequest{Create: arg1} + response, res1 := set.CreateEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*CreateResponse).User, res1 +} + +func (set EndpointsSet) Get(arg0 context.Context, arg1 string) (res0 *users.User, res1 error) { + request := GetRequest{UserId: arg1} + response, res1 := set.GetEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*GetResponse).User, res1 +} + +func (set EndpointsSet) Find(arg0 context.Context, arg1 *users.Filter, arg2 *options.FindOptions) (res0 []*users.User, res1 int, res2 error) { + request := FindRequest{ + Filter: arg1, + Options: arg2, + } + response, res2 := set.FindEndpoint(arg0, &request) + if res2 != nil { + if e, ok := status.FromError(res2); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res2 = errors.New(e.Message()) + } + return + } + return response.(*FindResponse).Users, response.(*FindResponse).Total, res2 +} + +func (set EndpointsSet) Update(arg0 context.Context, arg1 *users.User) (res0 error) { + request := UpdateRequest{Update: arg1} + _, res0 = set.UpdateEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} + +func (set EndpointsSet) Delete(arg0 context.Context, arg1 string) (res0 error) { + request := DeleteRequest{UserId: arg1} + _, res0 = set.DeleteEndpoint(arg0, &request) + if res0 != nil { + if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res0 = errors.New(e.Message()) + } + return + } + return res0 +} + +func (set EndpointsSet) GetByIdentity(arg0 context.Context, arg1 string) (res0 *users.User, res1 error) { + request := GetByIdentityRequest{Identity: arg1} + response, res1 := set.GetByIdentityEndpoint(arg0, &request) + if res1 != nil { + if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown { + res1 = errors.New(e.Message()) + } + return + } + return response.(*GetByIdentityResponse).User, res1 +} diff --git a/pkg/users/transport/endpoints.microgen.go b/pkg/users/transport/endpoints.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..264025bfc25edd39b423f5ca983188bc6e3a9e60 --- /dev/null +++ b/pkg/users/transport/endpoints.microgen.go @@ -0,0 +1,15 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import endpoint "github.com/go-kit/kit/endpoint" + +// EndpointsSet implements Users API and used for transport purposes. +type EndpointsSet struct { + CreateEndpoint endpoint.Endpoint + GetEndpoint endpoint.Endpoint + FindEndpoint endpoint.Endpoint + UpdateEndpoint endpoint.Endpoint + DeleteEndpoint endpoint.Endpoint + GetByIdentityEndpoint endpoint.Endpoint +} diff --git a/pkg/users/transport/exchanges.microgen.go b/pkg/users/transport/exchanges.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..f70b8cdfe93eb8b843ec66136e8fa63bccc735ee --- /dev/null +++ b/pkg/users/transport/exchanges.microgen.go @@ -0,0 +1,52 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + options "git.perx.ru/perxis/perxis-go/pkg/options" + users "git.perx.ru/perxis/perxis-go/pkg/users" +) + +type ( + CreateRequest struct { + Create *users.User `json:"create"` + } + CreateResponse struct { + User *users.User `json:"user"` + } + + GetRequest struct { + UserId string `json:"user_id"` + } + GetResponse struct { + User *users.User `json:"user"` + } + + FindRequest struct { + Filter *users.Filter `json:"filter"` + Options *options.FindOptions `json:"options"` + } + FindResponse struct { + Users []*users.User `json:"users"` + Total int `json:"total"` + } + + UpdateRequest struct { + Update *users.User `json:"update"` + } + // Formal exchange type, please do not delete. + UpdateResponse struct{} + + DeleteRequest struct { + UserId string `json:"user_id"` + } + // Formal exchange type, please do not delete. + DeleteResponse struct{} + + GetByIdentityRequest struct { + Identity string `json:"identity"` + } + GetByIdentityResponse struct { + User *users.User `json:"user"` + } +) diff --git a/pkg/users/transport/grpc/client.microgen.go b/pkg/users/transport/grpc/client.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..98f4b74c1d23e667d8c1d2c7290cd136f5e20a5d --- /dev/null +++ b/pkg/users/transport/grpc/client.microgen.go @@ -0,0 +1,61 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/users/transport" + pb "git.perx.ru/perxis/perxis-go/proto/users" + grpckit "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" +) + +func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet { + if addr == "" { + addr = "account.users.Users" + } + return transport.EndpointsSet{ + CreateEndpoint: grpckit.NewClient( + conn, addr, "Create", + _Encode_Create_Request, + _Decode_Create_Response, + pb.CreateResponse{}, + opts..., + ).Endpoint(), + DeleteEndpoint: grpckit.NewClient( + conn, addr, "Delete", + _Encode_Delete_Request, + _Decode_Delete_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + FindEndpoint: grpckit.NewClient( + conn, addr, "Find", + _Encode_Find_Request, + _Decode_Find_Response, + pb.FindResponse{}, + opts..., + ).Endpoint(), + GetByIdentityEndpoint: grpckit.NewClient( + conn, addr, "GetByIdentity", + _Encode_GetByIdentity_Request, + _Decode_GetByIdentity_Response, + pb.GetByIdentityResponse{}, + opts..., + ).Endpoint(), + GetEndpoint: grpckit.NewClient( + conn, addr, "Get", + _Encode_Get_Request, + _Decode_Get_Response, + pb.GetResponse{}, + opts..., + ).Endpoint(), + UpdateEndpoint: grpckit.NewClient( + conn, addr, "Update", + _Encode_Update_Request, + _Decode_Update_Response, + empty.Empty{}, + opts..., + ).Endpoint(), + } +} diff --git a/pkg/users/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/users/transport/grpc/protobuf_endpoint_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..1837d41081572ab74ebfd7c9594a8292b4c9de29 --- /dev/null +++ b/pkg/users/transport/grpc/protobuf_endpoint_converters.microgen.go @@ -0,0 +1,265 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// Please, do not change functions names! +package transportgrpc + +import ( + "context" + "errors" + + transport "git.perx.ru/perxis/perxis-go/pkg/users/transport" + pb "git.perx.ru/perxis/perxis-go/proto/users" + empty "github.com/golang/protobuf/ptypes/empty" +) + +func _Encode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil CreateRequest") + } + req := request.(*transport.CreateRequest) + reqCreate, err := PtrUserToProto(req.Create) + if err != nil { + return nil, err + } + return &pb.CreateRequest{Create: reqCreate}, nil +} + +func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetRequest") + } + req := request.(*transport.GetRequest) + return &pb.GetRequest{UserId: req.UserId}, nil +} + +func _Encode_Find_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil FindRequest") + } + req := request.(*transport.FindRequest) + reqFilter, err := PtrFilterToProto(req.Filter) + if err != nil { + return nil, err + } + reqOptions, err := PtrServicesFindOptionsToProto(req.Options) + if err != nil { + return nil, err + } + return &pb.FindRequest{ + Filter: reqFilter, + Options: reqOptions, + }, nil +} + +func _Encode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil UpdateRequest") + } + req := request.(*transport.UpdateRequest) + reqUpdate, err := PtrUserToProto(req.Update) + if err != nil { + return nil, err + } + return &pb.UpdateRequest{Update: reqUpdate}, nil +} + +func _Encode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil DeleteRequest") + } + req := request.(*transport.DeleteRequest) + return &pb.DeleteRequest{UserId: req.UserId}, nil +} + +func _Encode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil CreateResponse") + } + resp := response.(*transport.CreateResponse) + respUser, err := PtrUserToProto(resp.User) + if err != nil { + return nil, err + } + return &pb.CreateResponse{User: respUser}, nil +} + +func _Encode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetResponse") + } + resp := response.(*transport.GetResponse) + respUser, err := PtrUserToProto(resp.User) + if err != nil { + return nil, err + } + return &pb.GetResponse{User: respUser}, nil +} + +func _Encode_Find_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil FindResponse") + } + resp := response.(*transport.FindResponse) + respUsers, err := ListPtrUserToProto(resp.Users) + if err != nil { + return nil, err + } + return &pb.FindResponse{ + Total: int64(resp.Total), + Users: respUsers, + }, nil +} + +func _Encode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_Create_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil CreateRequest") + } + req := request.(*pb.CreateRequest) + reqCreate, err := ProtoToPtrUser(req.Create) + if err != nil { + return nil, err + } + return &transport.CreateRequest{Create: reqCreate}, nil +} + +func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetRequest") + } + req := request.(*pb.GetRequest) + return &transport.GetRequest{UserId: string(req.UserId)}, nil +} + +func _Decode_Find_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil FindRequest") + } + req := request.(*pb.FindRequest) + reqFilter, err := ProtoToPtrFilter(req.Filter) + if err != nil { + return nil, err + } + reqOptions, err := ProtoToPtrServicesFindOptions(req.Options) + if err != nil { + return nil, err + } + return &transport.FindRequest{ + Filter: reqFilter, + Options: reqOptions, + }, nil +} + +func _Decode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil UpdateRequest") + } + req := request.(*pb.UpdateRequest) + reqUpdate, err := ProtoToPtrUser(req.Update) + if err != nil { + return nil, err + } + return &transport.UpdateRequest{Update: reqUpdate}, nil +} + +func _Decode_Delete_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil DeleteRequest") + } + req := request.(*pb.DeleteRequest) + return &transport.DeleteRequest{UserId: string(req.UserId)}, nil +} + +func _Decode_Create_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil CreateResponse") + } + resp := response.(*pb.CreateResponse) + respUser, err := ProtoToPtrUser(resp.User) + if err != nil { + return nil, err + } + return &transport.CreateResponse{User: respUser}, nil +} + +func _Decode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetResponse") + } + resp := response.(*pb.GetResponse) + respUser, err := ProtoToPtrUser(resp.User) + if err != nil { + return nil, err + } + return &transport.GetResponse{User: respUser}, nil +} + +func _Decode_Find_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil FindResponse") + } + resp := response.(*pb.FindResponse) + respUsers, err := ProtoToListPtrUser(resp.Users) + if err != nil { + return nil, err + } + return &transport.FindResponse{ + Total: int(resp.Total), + Users: respUsers, + }, nil +} + +func _Decode_Delete_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Encode_GetByIdentity_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetByIdentityRequest") + } + req := request.(*transport.GetByIdentityRequest) + return &pb.GetByIdentityRequest{Identity: req.Identity}, nil +} + +func _Encode_GetByIdentity_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetByIdentityResponse") + } + resp := response.(*transport.GetByIdentityResponse) + respUser, err := PtrUserToProto(resp.User) + if err != nil { + return nil, err + } + return &pb.GetByIdentityResponse{User: respUser}, nil +} + +func _Decode_GetByIdentity_Request(ctx context.Context, request interface{}) (interface{}, error) { + if request == nil { + return nil, errors.New("nil GetByIdentityRequest") + } + req := request.(*pb.GetByIdentityRequest) + return &transport.GetByIdentityRequest{Identity: string(req.Identity)}, nil +} + +func _Decode_GetByIdentity_Response(ctx context.Context, response interface{}) (interface{}, error) { + if response == nil { + return nil, errors.New("nil GetByIdentityResponse") + } + resp := response.(*pb.GetByIdentityResponse) + respUser, err := ProtoToPtrUser(resp.User) + if err != nil { + return nil, err + } + return &transport.GetByIdentityResponse{User: respUser}, nil +} + +func _Encode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} + +func _Decode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) { + return &empty.Empty{}, nil +} diff --git a/pkg/users/transport/grpc/protobuf_type_converters.microgen.go b/pkg/users/transport/grpc/protobuf_type_converters.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..d0789e52420b5383530e10f89e06100197a32bbb --- /dev/null +++ b/pkg/users/transport/grpc/protobuf_type_converters.microgen.go @@ -0,0 +1,153 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// It is better for you if you do not change functions names! +// This file will never be overwritten. +package transportgrpc + +import ( + options "git.perx.ru/perxis/perxis-go/pkg/options" + service "git.perx.ru/perxis/perxis-go/pkg/users" + common "git.perx.ru/perxis/perxis-go/proto/common" + pb "git.perx.ru/perxis/perxis-go/proto/users" + "github.com/golang/protobuf/ptypes/wrappers" +) + +func PtrUserToProto(create *service.User) (*pb.User, error) { + if create == nil { + return nil, nil + } + u := &pb.User{ + Id: create.ID, + Name: create.Name, + Identities: create.Identities, + DisplayName: create.DisplayName, + Email: create.Email, + AvatarUrl: create.AvatarURL, + } + if create.EmailVerified != nil { + u.EmailVerified = &wrappers.BoolValue{ + Value: *create.EmailVerified, + } + } + if create.System != nil { + u.System = &wrappers.BoolValue{ + Value: *create.System, + } + } + return u, nil +} + +func ProtoToPtrUser(protoCreate *pb.User) (*service.User, error) { + if protoCreate == nil { + return nil, nil + } + user := &service.User{ + ID: protoCreate.Id, + Name: protoCreate.Name, + DisplayName: protoCreate.DisplayName, + Identities: protoCreate.Identities, + Email: protoCreate.Email, + AvatarURL: protoCreate.AvatarUrl, + } + if protoCreate.EmailVerified != nil { + user.EmailVerified = &protoCreate.EmailVerified.Value + } + if protoCreate.System != nil { + user.System = &protoCreate.System.Value + } + return user, nil +} + +func PtrFilterToProto(filter *service.Filter) (*pb.Filter, error) { + if filter == nil { + return nil, nil + } + f := &pb.Filter{ + Id: filter.ID, + Name: filter.Name, + Identities: filter.Identities, + DisplayName: filter.DisplayName, + Email: filter.Email, + } + if filter.EmailVerified != nil { + f.EmailVerified = &wrappers.BoolValue{ + Value: *filter.EmailVerified, + } + } + if filter.System != nil { + f.System = &wrappers.BoolValue{ + Value: *filter.System, + } + } + return f, nil +} + +func ProtoToPtrFilter(protoFilter *pb.Filter) (*service.Filter, error) { + if protoFilter == nil { + return nil, nil + } + f := &service.Filter{ + ID: protoFilter.Id, + Name: protoFilter.Name, + Identities: protoFilter.Identities, + DisplayName: protoFilter.DisplayName, + Email: protoFilter.Email, + } + if protoFilter.EmailVerified != nil { + f.EmailVerified = &protoFilter.EmailVerified.Value + } + if protoFilter.System != nil { + f.System = &protoFilter.System.Value + } + return f, nil +} + +func ListPtrUserToProto(users []*service.User) ([]*pb.User, error) { + protoUsers := make([]*pb.User, 0, len(users)) + for _, u := range users { + pu, err := PtrUserToProto(u) + if err != nil { + return nil, err + } + protoUsers = append(protoUsers, pu) + } + return protoUsers, nil +} + +func ProtoToListPtrUser(protoCreates []*pb.User) ([]*service.User, error) { + users := make([]*service.User, 0, len(protoCreates)) + for _, pu := range protoCreates { + u, err := ProtoToPtrUser(pu) + if err != nil { + return nil, err + } + users = append(users, u) + } + return users, nil +} + +func PtrServicesFindOptionsToProto(opts *options.FindOptions) (*common.FindOptions, error) { + if opts == nil { + return nil, nil + } + return &common.FindOptions{ + Sort: opts.Sort, + PageNum: int32(opts.PageNum), + PageSize: int32(opts.PageSize), + }, nil +} + +func ProtoToPtrServicesFindOptions(protoOpts *common.FindOptions) (*options.FindOptions, error) { + if protoOpts == nil { + return nil, nil + } + return &options.FindOptions{ + SortOptions: options.SortOptions{ + Sort: protoOpts.Sort, + }, + PaginationOptions: options.PaginationOptions{ + PageNum: int(protoOpts.PageNum), + PageSize: int(protoOpts.PageSize), + }, + }, nil +} diff --git a/pkg/users/transport/grpc/server.microgen.go b/pkg/users/transport/grpc/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..2be01e97a5a98fcf5189b386fbb36d1a60f43db7 --- /dev/null +++ b/pkg/users/transport/grpc/server.microgen.go @@ -0,0 +1,112 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +// DO NOT EDIT. +package transportgrpc + +import ( + transport "git.perx.ru/perxis/perxis-go/pkg/users/transport" + pb "git.perx.ru/perxis/perxis-go/proto/users" + grpc "github.com/go-kit/kit/transport/grpc" + empty "github.com/golang/protobuf/ptypes/empty" + context "golang.org/x/net/context" +) + +type usersServer struct { + create grpc.Handler + get grpc.Handler + find grpc.Handler + update grpc.Handler + delete grpc.Handler + getByIdentity grpc.Handler + + pb.UnimplementedUsersServer +} + +func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.UsersServer { + return &usersServer{ + create: grpc.NewServer( + endpoints.CreateEndpoint, + _Decode_Create_Request, + _Encode_Create_Response, + opts..., + ), + delete: grpc.NewServer( + endpoints.DeleteEndpoint, + _Decode_Delete_Request, + _Encode_Delete_Response, + opts..., + ), + find: grpc.NewServer( + endpoints.FindEndpoint, + _Decode_Find_Request, + _Encode_Find_Response, + opts..., + ), + get: grpc.NewServer( + endpoints.GetEndpoint, + _Decode_Get_Request, + _Encode_Get_Response, + opts..., + ), + getByIdentity: grpc.NewServer( + endpoints.GetByIdentityEndpoint, + _Decode_GetByIdentity_Request, + _Encode_GetByIdentity_Response, + opts..., + ), + update: grpc.NewServer( + endpoints.UpdateEndpoint, + _Decode_Update_Request, + _Encode_Update_Response, + opts..., + ), + } +} + +func (S *usersServer) Create(ctx context.Context, req *pb.CreateRequest) (*pb.CreateResponse, error) { + _, resp, err := S.create.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.CreateResponse), nil +} + +func (S *usersServer) Get(ctx context.Context, req *pb.GetRequest) (*pb.GetResponse, error) { + _, resp, err := S.get.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.GetResponse), nil +} + +func (S *usersServer) Find(ctx context.Context, req *pb.FindRequest) (*pb.FindResponse, error) { + _, resp, err := S.find.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.FindResponse), nil +} + +func (S *usersServer) Update(ctx context.Context, req *pb.UpdateRequest) (*empty.Empty, error) { + _, resp, err := S.update.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} + +func (S *usersServer) Delete(ctx context.Context, req *pb.DeleteRequest) (*empty.Empty, error) { + _, resp, err := S.delete.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*empty.Empty), nil +} + +func (S *usersServer) GetByIdentity(ctx context.Context, req *pb.GetByIdentityRequest) (*pb.GetByIdentityResponse, error) { + _, resp, err := S.getByIdentity.ServeGRPC(ctx, req) + if err != nil { + return nil, err + } + return resp.(*pb.GetByIdentityResponse), nil +} diff --git a/pkg/users/transport/server.microgen.go b/pkg/users/transport/server.microgen.go new file mode 100644 index 0000000000000000000000000000000000000000..e12645efc79921da957e359848d9dca606f6364b --- /dev/null +++ b/pkg/users/transport/server.microgen.go @@ -0,0 +1,72 @@ +// Code generated by microgen 0.9.1. DO NOT EDIT. + +package transport + +import ( + "context" + + users "git.perx.ru/perxis/perxis-go/pkg/users" + endpoint "github.com/go-kit/kit/endpoint" +) + +func Endpoints(svc users.Users) EndpointsSet { + return EndpointsSet{ + CreateEndpoint: CreateEndpoint(svc), + DeleteEndpoint: DeleteEndpoint(svc), + FindEndpoint: FindEndpoint(svc), + GetByIdentityEndpoint: GetByIdentityEndpoint(svc), + GetEndpoint: GetEndpoint(svc), + UpdateEndpoint: UpdateEndpoint(svc), + } +} + +func CreateEndpoint(svc users.Users) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*CreateRequest) + res0, res1 := svc.Create(arg0, req.Create) + return &CreateResponse{User: res0}, res1 + } +} + +func GetEndpoint(svc users.Users) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*GetRequest) + res0, res1 := svc.Get(arg0, req.UserId) + return &GetResponse{User: res0}, res1 + } +} + +func FindEndpoint(svc users.Users) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*FindRequest) + res0, res1, res2 := svc.Find(arg0, req.Filter, req.Options) + return &FindResponse{ + Total: res1, + Users: res0, + }, res2 + } +} + +func UpdateEndpoint(svc users.Users) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*UpdateRequest) + res0 := svc.Update(arg0, req.Update) + return &UpdateResponse{}, res0 + } +} + +func DeleteEndpoint(svc users.Users) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*DeleteRequest) + res0 := svc.Delete(arg0, req.UserId) + return &DeleteResponse{}, res0 + } +} + +func GetByIdentityEndpoint(svc users.Users) endpoint.Endpoint { + return func(arg0 context.Context, request interface{}) (interface{}, error) { + req := request.(*GetByIdentityRequest) + res0, res1 := svc.GetByIdentity(arg0, req.Identity) + return &GetByIdentityResponse{User: res0}, res1 + } +} diff --git a/pkg/users/user.go b/pkg/users/user.go new file mode 100644 index 0000000000000000000000000000000000000000..9eca6efc24569c2ef951d2bcb059440d54896ecc --- /dev/null +++ b/pkg/users/user.go @@ -0,0 +1,31 @@ +package users + +// Current - Идентификатор, который можно иÑпользовать Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ/обновлениÑ/региÑтрации +// пользователÑ, от имени которого был Ñделан запроÑ. +const Current = "current" + +type User struct { + ID string `json:"id" bson:"_id"` + Name string `json:"name" bson:"name"` + DisplayName string `json:"displayName" bson:"displayName"` + Identities []string `json:"identities" bson:"identities"` + Email string `json:"email" bson:"email"` + EmailVerified *bool `json:"emailVerified" bson:"emailVerified"` + AvatarURL string `json:"avatarUrl" bson:"avatarUrl,omitempty"` + System *bool `json:"system" bson:"system"` +} + +func (u User) GetID() string { + return u.ID +} + +func (u User) IsSystem() bool { + if u.System != nil { + return *u.System + } + return false +} + +func (u User) Clone() *User { + return &u +}