From babb5580f696eb088668ed747fc375304c185865 Mon Sep 17 00:00:00 2001 From: Alena Petraki <alena.petraki@gmail.com> Date: Thu, 13 Apr 2023 14:29:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D1=8B=20expr,=20i?= =?UTF-8?q?d,=20permission?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 21 +- go.sum | 246 +++--------- pkg/errors/error.go | 51 --- pkg/errors/errors.go | 55 --- pkg/expr/bench.txt | 30 ++ pkg/expr/config.go | 85 +++++ pkg/expr/context.go | 56 +++ pkg/expr/exp.go | 65 ++++ pkg/expr/expr.go | 58 +++ pkg/expr/format.go | 91 +++++ pkg/expr/format_test.go | 45 +++ pkg/expr/mongo.go | 665 +++++++++++++++++++++++++++++++++ pkg/expr/mongo_test.go | 86 +++++ pkg/expr/slice.go | 54 +++ pkg/expr/slice_test.go | 62 +++ pkg/expr/string.go | 50 +++ pkg/expr/string_test.go | 84 +++++ pkg/expr/time.go | 79 ++++ pkg/id/id.go | 7 + pkg/permission/permission.go | 64 ++++ pkg/permission/ruleset.go | 266 +++++++++++++ pkg/permission/ruleset_test.go | 51 +++ 22 files changed, 1963 insertions(+), 308 deletions(-) delete mode 100644 pkg/errors/error.go create mode 100644 pkg/expr/bench.txt create mode 100644 pkg/expr/config.go create mode 100644 pkg/expr/context.go create mode 100644 pkg/expr/exp.go create mode 100644 pkg/expr/expr.go create mode 100644 pkg/expr/format.go create mode 100644 pkg/expr/format_test.go create mode 100644 pkg/expr/mongo.go create mode 100644 pkg/expr/mongo_test.go create mode 100644 pkg/expr/slice.go create mode 100644 pkg/expr/slice_test.go create mode 100644 pkg/expr/string.go create mode 100644 pkg/expr/string_test.go create mode 100644 pkg/expr/time.go create mode 100644 pkg/id/id.go create mode 100644 pkg/permission/permission.go create mode 100644 pkg/permission/ruleset.go create mode 100644 pkg/permission/ruleset_test.go diff --git a/go.mod b/go.mod index e23b785d..4a82fbec 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,20 @@ module git.perx.ru/perxis/perxis-go go 1.18 require ( - github.com/getsentry/sentry-go v0.12.0 + 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/json-iterator/go v1.1.12 github.com/pkg/errors v0.9.1 github.com/rs/xid v1.4.0 - github.com/stretchr/testify v1.7.0 - go.uber.org/zap v1.19.1 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 - golang.org/x/net v0.0.0-20211008194852-3b03d305991f + github.com/stretchr/testify v1.8.0 + go.mongodb.org/mongo-driver v1.11.4 + 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.0-20210107192922-496545a6307b + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -24,13 +24,10 @@ require ( 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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.1.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.7.0 // indirect + github.com/stretchr/objx v0.4.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 diff --git a/go.sum b/go.sum index 0ab6716f..1f26de45 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,11 @@ 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/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= -github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= -github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +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/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU= +github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8= 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= @@ -24,18 +17,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/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +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/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= 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= @@ -43,34 +30,18 @@ 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/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk= -github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= +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/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 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= github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -84,211 +55,118 @@ 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/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +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.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/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +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/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= -github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= -github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= -github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= -github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= -github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= -github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= -github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +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/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= -github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= -github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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.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/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +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/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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.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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= -github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +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-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +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= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f h1:1scJEYZBaF48BaG6tYbtxmLcXqwYGSfGcMoStTqkkIw= -golang.org/x/net v0.0.0-20211008194852-3b03d305991f/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= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223/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-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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-20210403161142-5e06dd20ab57/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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= @@ -299,14 +177,10 @@ 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/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -349,21 +223,13 @@ google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscL google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -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-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -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/errors/error.go b/pkg/errors/error.go deleted file mode 100644 index cb8ec386..00000000 --- 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 952cd868..1b3fe1b2 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 00000000..0a8b9b81 --- /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 00000000..62811117 --- /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 00000000..4049d96f --- /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 00000000..e34b00b8 --- /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 00000000..9c788e71 --- /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 00000000..730388d2 --- /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 00000000..7a0ba4c6 --- /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 00000000..02075dc4 --- /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{{"$geometry", map[string]interface{}{"type": "Point", "coordinates": point}}, {"$maxDistance", 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 00000000..26526945 --- /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{{"$geometry", map[string]interface{}{"coordinates": []interface{}{55.5, 37.5}, "type": "Point"}}, {"$maxDistance", 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 00000000..d286e146 --- /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 00000000..533c2beb --- /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 00000000..0e857b41 --- /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 00000000..ee8e69fa --- /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 00000000..740da301 --- /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 00000000..cfb8b639 --- /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/permission/permission.go b/pkg/permission/permission.go new file mode 100644 index 00000000..7a90e507 --- /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 00000000..b9a84f37 --- /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 00000000..47c1f076 --- /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) + }) + } +} -- GitLab