diff --git a/.github/workflows/before_script.sh b/.github/workflows/before_script.sh new file mode 100644 index 000000000..b0bbee274 --- /dev/null +++ b/.github/workflows/before_script.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +find . -name "*.go" | xargs gofmt -w +git diff --name-only --exit-code || if [ $? != 0 ]; then echo "Notice: gofmt check failed,please gofmt before pr." && exit 1; fi +echo "gofmt check pass." +sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts \ No newline at end of file diff --git a/.github/workflows/build_and_test.sh b/.github/workflows/build_and_test.sh new file mode 100644 index 000000000..5d33f71aa --- /dev/null +++ b/.github/workflows/build_and_test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +GOARCH=${{ matrix.goarch }} +for file in `find . -name go.mod`; do + dirpath=$(dirname $file) + echo $dirpath + + # package oracle needs golang >= v1.17 + if [ "oracle" = $(basename $dirpath) ]; then + if ! go version|grep -q "1.17"; then + echo "ignore oracle as go version: $(go version)" + continue 1 + fi + fi + + # package kuhecm needs golang >= v1.18 + if [ "kubecm" = $(basename $dirpath) ]; then + if ! go version|grep -q "1.18"; then + echo "ignore kubecm as go version: $(go version)" + continue 1 + fi + fi + + # package example needs golang >= v1.18 + if [ "example" = $(basename $dirpath) ]; then + if ! go version|grep -q "1.18"; then + echo "ignore example as go version: $(go version)" + continue 1 + fi + fi + + cd $dirpath + go mod tidy + go build ./... + go test ./... -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1 + if grep -q "/gogf/gf/.*/v2" go.mod; then + sed -i "s/gogf\/gf\(\/.*\)\/v2/gogf\/gf\/v2\1/g" coverage.out + fi + cd - +done \ No newline at end of file diff --git a/.github/workflows/gf.yml b/.github/workflows/gf.yml index bce8c6d6a..9e8a7c36a 100644 --- a/.github/workflows/gf.yml +++ b/.github/workflows/gf.yml @@ -58,6 +58,7 @@ jobs: - 3306:3306 # PostgreSQL backend server. + # docker run -d --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=12345678 -e POSTGRES_USER=postgres -e POSTGRES_DB=test -v postgres:/Users/john/Temp/postgresql/data loads/postgres:13 postgres: image: loads/postgres:13 env: @@ -101,12 +102,12 @@ jobs: - 9001:9001 polaris: - image: polarismesh/polaris-server-standalone:latest + image: loads/polaris-server-standalone:latest ports: - 8090:8090 - 8091:8091 - #oracle 11g server + # oracle 11g server oracle-server: image: loads/oracle-xe-11g-r2:latest env: @@ -117,6 +118,11 @@ jobs: ports: - 1521:1521 + # dm8 server + dm-server: + image: loads/dm:v8.1.2.128_ent_x86_64_ctm_pack4 + ports: + - 5236:5236 strategy: matrix: @@ -132,6 +138,9 @@ jobs: - name: Checkout Repository uses: actions/checkout@v2 + - name: Start Minikube + uses: medyagh/setup-minikube@master + - name: Setup Golang ${{ matrix.go-version }} uses: actions/setup-go@v2 with: @@ -140,11 +149,6 @@ jobs: - name: Setup Golang caches uses: actions/cache@v3 with: - # In order: - # * Module download cache - # * Build cache (Linux) - # * Build cache (Mac) - # * Build cache (Windows) path: | ~/go/pkg/mod ~/.cache/go-build @@ -155,39 +159,16 @@ jobs: ${{ runner.os }}-go-${{ matrix.go-version }}- - name: Start containers - run: docker-compose -f ".github/workflows/docker/docker-compose.yml" up -d --build + run: docker-compose -f ".github/workflows/docker/docker-compose.yml" up -d --build - name: Before Script - run: | - find . -name "*.go" | xargs gofmt -w - git diff --name-only --exit-code || if [ $? != 0 ]; then echo "Notice: gofmt check failed,please gofmt before pr." && exit 1; fi - echo "gofmt check pass." - sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts + run: bash .github/workflows/before_script.sh - name: Build & Test - run: | - GOARCH=${{ matrix.goarch }} - for file in `find . -name go.mod`; do - dirpath=$(dirname $file) - - if [ "oracle" = $(basename $dirpath) ]; then - if ! go version|grep -q "1.17"; then - continue 1 - fi - fi - - cd $dirpath - go mod tidy - go build ./... - go test ./... -race -coverprofile=coverage.out -covermode=atomic -coverpkg=./...,github.com/gogf/gf/... || exit 1 - if grep -q "/gogf/gf/.*/v2" go.mod; then - sed -i "s/gogf\/gf\(\/.*\)\/v2/gogf\/gf\/v2\1/g" coverage.out - fi - cd - - done + run: bash .github/workflows/build_and_test.sh - name: Stop containers - run: docker-compose -f ".github/workflows/docker/docker-compose.yml" down + run: docker-compose -f ".github/workflows/docker/docker-compose.yml" down - name: Report Coverage uses: codecov/codecov-action@v2 diff --git a/container/gmap/gmap_hash_str_any_map.go b/container/gmap/gmap_hash_str_any_map.go index 24ec1cc3d..88ec164f4 100644 --- a/container/gmap/gmap_hash_str_any_map.go +++ b/container/gmap/gmap_hash_str_any_map.go @@ -16,6 +16,7 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) +// StrAnyMap implements map[string]interface{} with RWMutex that has switch. type StrAnyMap struct { mu rwmutex.RWMutex data map[string]interface{} diff --git a/contrib/config/kubecm/README.MD b/contrib/config/kubecm/README.MD new file mode 100644 index 000000000..937aeb7e5 --- /dev/null +++ b/contrib/config/kubecm/README.MD @@ -0,0 +1,152 @@ +# kubecm +Package `kubecm` implements GoFrame `gcfg.Adapter` using kubernetes configmap. + +# Limit + +```go +glang version >= v.18 +``` + +# Installation +``` +go get -u github.com/gogf/gf/contrib/config/kubecm/v2 +``` + +# Example + +## Example configmap +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-configmap +data: + config.yaml: | + # HTTP service. + server: + address: ":8888" + openapiPath: "/api.json" + swaggerPath: "/swagger" + accessLogEnabled: true +``` + +Note the configmap name `test-configmap`, and its item name in data `config.yaml`. + + +## Create a custom boot package + +It is strongly recommended creating a boot package, +which sets the Adapter of default configuration instance. + +### Running in pod (common scenario) +```go +package boot + +import ( + "github.com/gogf/gf/contrib/config/kubecm/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" +) + +const ( + configmapName = "test-configmap" + dataItemInConfigmap = "config.yaml" +) + +func init() { + var ( + err error + ctx = gctx.GetInitCtx() + ) + // Create kubecm Client that implements gcfg.Adapter. + adapter, err := kubecm.New(gctx.GetInitCtx(), kubecm.Config{ + ConfigMap: configmapName, + DataItem: dataItemInConfigmap, + }) + if err != nil { + g.Log().Fatalf(ctx, `%+v`, err) + } + + // Change the adapter of default configuration instance. + g.Cfg().SetAdapter(adapter) +} +``` + +### Running out of pod (often testing scenario) +```go +package boot + +import ( + "github.com/gogf/gf/contrib/config/kubecm/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + "k8s.io/client-go/kubernetes" +) + +const ( + namespace = "default" + configmapName = "test-configmap" + dataItemInConfigmap = "config.yaml" + kubeConfigFilePathJohn = `/Users/john/.kube/config` +) + +func init() { + var ( + err error + ctx = gctx.GetInitCtx() + kubeClient *kubernetes.Clientset + ) + // Create kubernetes client. + // It is optional creating kube client when its is running in pod. + kubeClient, err = kubecm.NewKubeClientFromPath(ctx, kubeConfigFilePathJohn) + if err != nil { + g.Log().Fatalf(ctx, `%+v`, err) + } + // Create kubecm Client that implements gcfg.Adapter. + adapter, err := kubecm.New(gctx.GetInitCtx(), kubecm.Config{ + ConfigMap: configmapName, + DataItem: dataItemInConfigmap, + Namespace: namespace, // It is optional specifying namespace when its is running in pod. + KubeClient: kubeClient, // It is optional specifying kube client when its is running in pod. + }) + if err != nil { + g.Log().Fatalf(ctx, `%+v`, err) + } + + // Change the adapter of default configuration instance. + g.Cfg().SetAdapter(adapter) + +} +``` + +## Import boot package in top of main + +It is strongly recommended import your boot package in top of your `main.go`. + +Note the top `import`: `_ "github.com/gogf/gf/example/config/kubecm/boot_in_pod"` . + +```go +package main + +import ( + _ "github.com/gogf/gf/example/config/kubecm/boot_in_pod" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" +) + +func main() { + var ctx = gctx.GetInitCtx() + + // Available checks. + g.Dump(g.Cfg().Available(ctx)) + + // All key-value configurations. + g.Dump(g.Cfg().Data(ctx)) + + // Retrieve certain value by key. + g.Dump(g.Cfg().MustGet(ctx, "server.address")) +} + +``` + diff --git a/contrib/config/kubecm/go.mod b/contrib/config/kubecm/go.mod new file mode 100644 index 000000000..e94f97a59 --- /dev/null +++ b/contrib/config/kubecm/go.mod @@ -0,0 +1,70 @@ +module github.com/gogf/gf/contrib/config/kubecm/v2 + +go 1.18 + +require ( + github.com/gogf/gf/v2 v2.0.0 + k8s.io/api v0.25.2 + k8s.io/apimachinery v0.25.2 + k8s.io/client-go v0.25.2 +) + +require ( + github.com/BurntSushi/toml v1.1.0 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/clbanning/mxj/v2 v2.5.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/gofuzz v1.1.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grokify/html-strip-tags-go v0.0.1 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-colorable v0.1.9 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.opentelemetry.io/otel v1.7.0 // indirect + go.opentelemetry.io/otel/sdk v1.7.0 // indirect + go.opentelemetry.io/otel/trace v1.7.0 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect + golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.70.1 // indirect + k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect + k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.2.0 // indirect +) + +replace github.com/gogf/gf/v2 => ../../../ diff --git a/contrib/config/kubecm/go.sum b/contrib/config/kubecm/go.sum new file mode 100644 index 000000000..91300d076 --- /dev/null +++ b/contrib/config/kubecm/go.sum @@ -0,0 +1,569 @@ +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= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +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= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +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.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +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.4.1/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.1/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/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= +github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= +go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= +go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/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-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +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-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y= +golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/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-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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= +k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= +k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= +k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= +k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= +k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/contrib/config/kubecm/kubecm.go b/contrib/config/kubecm/kubecm.go new file mode 100644 index 000000000..8f80ce002 --- /dev/null +++ b/contrib/config/kubecm/kubecm.go @@ -0,0 +1,132 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package kubecm implements gcfg.Adapter using kubernetes configmap. +package kubecm + +import ( + "context" + + kubeMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcfg" + "github.com/gogf/gf/v2/util/gutil" +) + +// Client implements gcfg.Adapter. +type Client struct { + Config // Config object when created. + value *g.Var // Configmap content cached. It is `*gjson.Json` value internally. +} + +// Config for Client. +type Config struct { + ConfigMap string `v:"required"` // ConfigMap name. + DataItem string `v:"required"` // DataItem is the key item in Configmap data. + Namespace string // (Optional) Specify the namespace for configmap. + RestConfig *rest.Config // (Optional) Custom rest config for kube client. + KubeClient *kubernetes.Clientset // (Optional) Custom kube client. +} + +// New creates and returns gcfg.Adapter implementing using kubernetes configmap. +func New(ctx context.Context, config Config) (adapter gcfg.Adapter, err error) { + // Data validation. + err = g.Validator().Data(config).Run(ctx) + if err != nil { + return nil, err + } + // Kubernetes client creating. + if config.KubeClient == nil { + if config.RestConfig == nil { + config.RestConfig, err = NewDefaultKubeConfig(ctx) + if err != nil { + return nil, gerror.Wrapf(err, `create kube config failed`) + } + } + config.KubeClient, err = kubernetes.NewForConfig(config.RestConfig) + if err != nil { + return nil, gerror.Wrapf(err, `create kube client failed`) + } + } + adapter = &Client{ + Config: config, + value: g.NewVar(nil, true), + } + return +} + +// Available checks and returns the backend configuration service is available. +// The optional parameter `resource` specifies certain configuration resource. +// +// Note that this function does not return error as it just does simply check for +// backend configuration service. +func (c *Client) Available(ctx context.Context, configMap ...string) (ok bool) { + err := c.init(ctx, configMap...) + return err == nil +} + +// Get retrieves and returns value by specified `pattern` in current resource. +// Pattern like: +// "x.y.z" for map item. +// "x.0.y" for slice item. +func (c *Client) Get(ctx context.Context, pattern string) (value interface{}, err error) { + if c.value.IsNil() { + if err = c.init(ctx); err != nil { + return nil, err + } + } + return c.value.Val().(*gjson.Json).Get(pattern).Val(), nil +} + +// Data retrieves and returns all configuration data in current resource as map. +// Note that this function may lead lots of memory usage if configuration data is too large, +// you can implement this function if necessary. +func (c *Client) Data(ctx context.Context) (data map[string]interface{}, err error) { + if c.value.IsNil() { + if err = c.init(ctx); err != nil { + return nil, err + } + } + return c.value.Val().(*gjson.Json).Map(), nil +} + +// init retrieves and caches the configmap content. +func (c *Client) init(ctx context.Context, configMap ...string) (err error) { + var ( + namespace = gutil.GetOrDefaultStr(Namespace(), c.Namespace) + configMapName = gutil.GetOrDefaultStr(c.ConfigMap, configMap...) + ) + cm, err := c.KubeClient.CoreV1().ConfigMaps(namespace).Get(ctx, configMapName, kubeMetaV1.GetOptions{}) + if err != nil { + return gerror.Wrapf( + err, + `retrieve configmap "%s" from namespace "%s" failed`, + configMapName, namespace, + ) + } + if c.value.IsNil() { + var j *gjson.Json + if c.DataItem != "" { + j, err = gjson.LoadContent(cm.Data[c.DataItem]) + if err != nil { + return gerror.Wrapf( + err, + `parse config map item from %s[%s] failed`, configMapName, c.DataItem, + ) + } + c.value.Set(j) + } else { + j = gjson.New(cm.Data) + c.value.Set(j) + } + } + return nil +} diff --git a/contrib/config/kubecm/kubecm_kube.go b/contrib/config/kubecm/kubecm_kube.go new file mode 100644 index 000000000..a2180b7e0 --- /dev/null +++ b/contrib/config/kubecm/kubecm_kube.go @@ -0,0 +1,64 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package kubecm + +import ( + "context" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + + "github.com/gogf/gf/v2/os/gfile" +) + +const ( + defaultKubernetesUserAgent = `kubecm.Client` + kubernetesNamespaceFilePath = `/var/run/secrets/kubernetes.io/serviceaccount/namespace` +) + +// Namespace retrieves and returns the namespace of current pod. +// Note that this function should be called in kubernetes pod. +func Namespace() string { + return gfile.GetContents(kubernetesNamespaceFilePath) +} + +// NewDefaultKubeClient creates and returns a default kubernetes client. +// It is commonly used when the service is running inside kubernetes cluster. +func NewDefaultKubeClient(ctx context.Context) (*kubernetes.Clientset, error) { + return NewKubeClientFromPath(ctx, "") +} + +// NewKubeClientFromPath creates and returns a kubernetes REST client by given `kubeConfigFilePath`. +func NewKubeClientFromPath(ctx context.Context, kubeConfigFilePath string) (*kubernetes.Clientset, error) { + restConfig, err := NewKubeConfigFromPath(ctx, kubeConfigFilePath) + if err != nil { + return nil, err + } + return kubernetes.NewForConfig(restConfig) +} + +// NewKubeClientFromConfig creates and returns client by given `rest.Config`. +func NewKubeClientFromConfig(ctx context.Context, config *rest.Config) (*kubernetes.Clientset, error) { + return kubernetes.NewForConfig(config) +} + +// NewDefaultKubeConfig creates and returns a default kubernetes config. +// It is commonly used when the service is running inside kubernetes cluster. +func NewDefaultKubeConfig(ctx context.Context) (*rest.Config, error) { + return NewKubeConfigFromPath(ctx, "") +} + +// NewKubeConfigFromPath creates and returns rest.Config object from given `kubeConfigFilePath`. +func NewKubeConfigFromPath(ctx context.Context, kubeConfigFilePath string) (*rest.Config, error) { + restConfig, err := clientcmd.BuildConfigFromFlags("", kubeConfigFilePath) + if err != nil { + return nil, err + } + restConfig.UserAgent = defaultKubernetesUserAgent + return restConfig, nil +} diff --git a/contrib/config/kubecm/kubecm_test.go b/contrib/config/kubecm/kubecm_test.go new file mode 100644 index 000000000..760e631b4 --- /dev/null +++ b/contrib/config/kubecm/kubecm_test.go @@ -0,0 +1,111 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package kubecm_test + +import ( + "testing" + + v1 "k8s.io/api/core/v1" + kubeMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/gogf/gf/contrib/config/kubecm/v2" + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/util/guid" +) + +const ( + namespace = "default" + configmap = "test-configmap" + dataItem = "config.yaml" + configmapFileName = "configmap.yaml" +) + +var ( + ctx = gctx.New() + kubeConfigFilePath = `/home/runner/.kube/config` + kubeConfigFilePathJohn = `/Users/john/.kube/config` +) + +func init() { + if !gfile.Exists(kubeConfigFilePath) { + kubeConfigFilePath = kubeConfigFilePathJohn + } +} + +func TestAvailable(t *testing.T) { + var ( + err error + kubeClient *kubernetes.Clientset + ) + // Configmap apply. + gtest.C(t, func(t *gtest.T) { + kubeClient, err = kubecm.NewKubeClientFromPath(ctx, kubeConfigFilePath) + t.AssertNil(err) + var ( + configMap v1.ConfigMap + content = gtest.DataContent(configmapFileName) + ) + err = gjson.New(content).Scan(&configMap) + t.AssertNil(err) + _, err = kubeClient.CoreV1().ConfigMaps(namespace).Create( + ctx, &configMap, kubeMetaV1.CreateOptions{}, + ) + t.AssertNil(err) + }) + defer func() { + gtest.C(t, func(t *gtest.T) { + err = kubeClient.CoreV1().ConfigMaps(namespace).Delete( + ctx, configmap, kubeMetaV1.DeleteOptions{}, + ) + t.AssertNil(err) + }) + }() + + gtest.C(t, func(t *gtest.T) { + adapter, err := kubecm.New(ctx, kubecm.Config{ + ConfigMap: configmap, + DataItem: dataItem, + Namespace: namespace, + KubeClient: kubeClient, + }) + t.AssertNil(err) + + config := g.Cfg(guid.S()) + config.SetAdapter(adapter) + + t.Assert(config.Available(ctx), true) + + m, err := config.Data(ctx) + t.AssertNil(err) + t.AssertGT(len(m), 0) + + v, err := config.Get(ctx, "server.address") + t.AssertNil(err) + t.Assert(v.String(), ":8888") + }) +} + +func TestNewKubeClientFromConfig(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + config, _ := kubecm.NewKubeConfigFromPath(ctx, kubeConfigFilePath) + _, err := kubecm.NewKubeClientFromConfig(ctx, config) + t.AssertNil(err) + }) +} + +// These functions should be called in pod environment, but it has no environment in CI UT testing. +// It so just calls them ,but does nothing. +func TestDefaultBehaviorFunctions(t *testing.T) { + kubecm.Namespace() + kubecm.NewDefaultKubeClient(ctx) + kubecm.NewDefaultKubeConfig(ctx) +} diff --git a/contrib/config/kubecm/testdata/configmap.yaml b/contrib/config/kubecm/testdata/configmap.yaml new file mode 100644 index 000000000..69b716b1c --- /dev/null +++ b/contrib/config/kubecm/testdata/configmap.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-configmap +data: + config.yaml: | + # HTTP service. + server: + address: ":8888" + openapiPath: "/api.json" + swaggerPath: "/swagger" + accessLogEnabled: true + + # Database configuration. + database: + logger: + level: "all" + stdout: true + user: + link: "mysql:root:12345678@tcp(mysql.default:3306)/user?loc=Local&parseTime=true" + debug: true + order: + link: "mysql:root:12345678@tcp(mysql.default:3306)/order?loc=Local&parseTime=true" + debug: true + + # Logger configuration. + logger: + level : "all" + stdout: true diff --git a/contrib/drivers/README.md b/contrib/drivers/README.MD similarity index 93% rename from contrib/drivers/README.md rename to contrib/drivers/README.MD index 86f4a39bb..4342dd67e 100644 --- a/contrib/drivers/README.md +++ b/contrib/drivers/README.MD @@ -77,6 +77,13 @@ Note: - It does not support `Transaction` feature. - It does not support `RowsAffected` feature. +## DM +``` +import _ "github.com/gogf/gf/contrib/drivers/dm/v2" +``` +Note: +- It does not support `Replace` features. +- # Custom Drivers It's quick and easy, please refer to current driver source. diff --git a/contrib/drivers/clickhouse/clickhouse.go b/contrib/drivers/clickhouse/clickhouse.go index 7b63acb73..9b0b548ed 100644 --- a/contrib/drivers/clickhouse/clickhouse.go +++ b/contrib/drivers/clickhouse/clickhouse.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package clickhouse implements gdb.Driver, which supports operations for ClickHouse. +// Package clickhouse implements gdb.Driver, which supports operations for database ClickHouse. package clickhouse import ( @@ -18,16 +18,17 @@ import ( "time" "github.com/ClickHouse/clickhouse-go/v2" - "github.com/gogf/gf/v2/container/gmap" - "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/util/gutil" + "github.com/google/uuid" + "github.com/shopspring/decimal" + + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gregex" - "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" - "github.com/google/uuid" ) // Driver is the driver for postgresql database. @@ -36,9 +37,6 @@ type Driver struct { } var ( - // tableFieldsMap caches the table information retrieved from database. - tableFieldsMap = gmap.New(true) - errUnsupportedInsertIgnore = errors.New("unsupported method:InsertIgnore") errUnsupportedInsertGetId = errors.New("unsupported method:InsertGetId") errUnsupportedReplace = errors.New("unsupported method:Replace") @@ -76,12 +74,16 @@ func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { } // Open creates and returns an underlying sql.DB object for clickhouse. -func (d *Driver) Open(config *gdb.ConfigNode) (*sql.DB, error) { +func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { + source := config.Link // clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60 if config.Link != "" { + // ============================================================================ + // Deprecated from v2.2.0. + // ============================================================================ // Custom changing the schema in runtime. if config.Name != "" { - config.Link, _ = gregex.ReplaceString(replaceSchemaPattern, "@$1/"+config.Name, config.Link) + source, _ = gregex.ReplaceString(replaceSchemaPattern, "@$1/"+config.Name, config.Link) } else { // If no schema, the link is matched for replacement dbName, _ := gregex.MatchString(replaceSchemaPattern, config.Link) @@ -89,21 +91,31 @@ func (d *Driver) Open(config *gdb.ConfigNode) (*sql.DB, error) { config.Name = dbName[len(dbName)-1] } } - } else if config.Pass != "" { - config.Link = fmt.Sprintf( - "clickhouse://%s:%s@%s:%s/%s?charset=%s&debug=%t", - config.User, url.PathEscape(config.Pass), config.Host, config.Port, config.Name, config.Charset, config.Debug) } else { - config.Link = fmt.Sprintf( - "clickhouse://%s@%s:%s/%s?charset=%s&debug=%t", - config.User, config.Host, config.Port, config.Name, config.Charset, config.Debug) + if config.Pass != "" { + source = fmt.Sprintf( + "clickhouse://%s:%s@%s:%s/%s?charset=%s&debug=%t", + config.User, url.PathEscape(config.Pass), + config.Host, config.Port, config.Name, config.Charset, config.Debug, + ) + } else { + source = fmt.Sprintf( + "clickhouse://%s@%s:%s/%s?charset=%s&debug=%t", + config.User, config.Host, config.Port, config.Name, config.Charset, config.Debug, + ) + } + if config.Extra != "" { + source = fmt.Sprintf("%s&%s", source, config.Extra) + } } - db, err := sql.Open(driverName, config.Link) - if err != nil { + if db, err = sql.Open(driverName, source); err != nil { + err = gerror.WrapCodef( + gcode.CodeDbOperationError, err, + `sql.Open failed for driver "%s" by source "%s"`, driverName, source, + ) return nil, err } - - return db, nil + return } // Tables retrieves and returns the tables of current schema. @@ -130,80 +142,48 @@ func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, func (d *Driver) TableFields( ctx context.Context, table string, schema ...string, ) (fields map[string]*gdb.TableField, err error) { - charL, charR := d.GetChars() - table = gstr.Trim(table, charL+charR) - if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") - } - useSchema := d.GetSchema() - if len(schema) > 0 && schema[0] != "" { - useSchema = schema[0] - } - v := tableFieldsMap.GetOrSetFuncLock( - fmt.Sprintf(`clickhouse_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), - func() interface{} { - var ( - result gdb.Result - link gdb.Link - ) - if link, err = d.SlaveLink(useSchema); err != nil { - return nil - } - var ( - columns = "name,position,default_expression,comment,type,is_in_partition_key,is_in_sorting_key,is_in_primary_key,is_in_sampling_key" - getColumnsSql = fmt.Sprintf( - "select %s from `system`.columns c where `table` = '%s'", - columns, table, - ) - ) - result, err = d.DoSelect(ctx, link, getColumnsSql) - if err != nil { - return nil - } - fields = make(map[string]*gdb.TableField) - for _, m := range result { - var ( - isNull = false - fieldType = m["type"].String() - ) - // in clickhouse , filed type like is Nullable(int) - fieldsResult, _ := gregex.MatchString(`^Nullable\((.*?)\)`, fieldType) - if len(fieldsResult) == 2 { - isNull = true - fieldType = fieldsResult[1] - } - fields[m["name"].String()] = &gdb.TableField{ - Index: m["position"].Int(), - Name: m["name"].String(), - Default: m["default_expression"].Val(), - Comment: m["comment"].String(), - //Key: m["Key"].String(), - Type: fieldType, - Null: isNull, - } - } - return fields - }, + var ( + result gdb.Result + link gdb.Link + useSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) ) - if v != nil { - fields = v.(map[string]*gdb.TableField) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil, err } - return -} - -// FilteredLink retrieves and returns filtered `linkInfo` that can be using for -// logging or tracing purpose. -func (d *Driver) FilteredLink() string { - linkInfo := d.GetConfig().Link - if linkInfo == "" { - return "" - } - s, _ := gregex.ReplaceString( - `(.+?):(.+)@tcp(.+)`, - `$1:xxx@tcp$3`, - linkInfo, + var ( + columns = "name,position,default_expression,comment,type,is_in_partition_key,is_in_sorting_key,is_in_primary_key,is_in_sampling_key" + getColumnsSql = fmt.Sprintf( + "select %s from `system`.columns c where `table` = '%s'", + columns, table, + ) ) - return s + result, err = d.DoSelect(ctx, link, getColumnsSql) + if err != nil { + return nil, err + } + fields = make(map[string]*gdb.TableField) + for _, m := range result { + var ( + isNull = false + fieldType = m["type"].String() + ) + // in clickhouse , filed type like is Nullable(int) + fieldsResult, _ := gregex.MatchString(`^Nullable\((.*?)\)`, fieldType) + if len(fieldsResult) == 2 { + isNull = true + fieldType = fieldsResult[1] + } + fields[m["name"].String()] = &gdb.TableField{ + Index: m["position"].Int(), + Name: m["name"].String(), + Default: m["default_expression"].Val(), + Comment: m["comment"].String(), + // Key: m["Key"].String(), + Type: fieldType, + Null: isNull, + } + } + return fields, nil } // PingMaster pings the master node to check authentication or keeps the connection alive. @@ -228,7 +208,7 @@ func (d *Driver) PingSlave() error { func (d *Driver) ping(conn *sql.DB) error { err := conn.Ping() if exception, ok := err.(*clickhouse.Exception); ok { - return errors.New(fmt.Sprintf("[%d]%s", exception.Code, exception.Message)) + return fmt.Errorf("[%d]%s", exception.Code, exception.Message) } return err } @@ -390,6 +370,15 @@ func (d *Driver) ConvertDataForRecord(ctx context.Context, value interface{}) (m m[k] = nil } + case decimal.Decimal: + m[k] = itemValue + + case *decimal.Decimal: + m[k] = nil + if itemValue != nil { + m[k] = *itemValue + } + default: // if the other type implements valuer for the driver package // the converted result is used diff --git a/contrib/drivers/clickhouse/clickhouse_test.go b/contrib/drivers/clickhouse/clickhouse_test.go index c3571f154..f94fd2d06 100644 --- a/contrib/drivers/clickhouse/clickhouse_test.go +++ b/contrib/drivers/clickhouse/clickhouse_test.go @@ -3,18 +3,19 @@ package clickhouse import ( "context" "fmt" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/guid" - "github.com/google/uuid" - "strings" "testing" "time" + "github.com/google/uuid" + "github.com/shopspring/decimal" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/test/gtest" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/grand" + "github.com/gogf/gf/v2/util/guid" ) const ( @@ -107,6 +108,8 @@ values (607970943242866688, 607973669943119880, 607972403489804288, 2022, 3, 20 , Col8 DateTime COMMENT '列8' , Col9 UUID COMMENT '列9' , Col10 DateTime COMMENT '列10' + , Col11 Decimal(9, 2) COMMENT '列11' + , Col12 Decimal(9, 2) COMMENT '列12' ) ENGINE = MergeTree() PRIMARY KEY Col4 ORDER BY Col4 @@ -131,6 +134,7 @@ func clickhouseLink() gdb.DB { connect, err := gdb.New(gdb.ConfigNode{ Link: "clickhouse://default@127.0.0.1:9000,127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60", Type: "clickhouse", + Name: "default", }) gtest.AssertNil(err) gtest.AssertNE(connect, nil) @@ -435,8 +439,12 @@ func TestDriverClickhouse_NilTime(t *testing.T) { Col8 *time.Time Col9 uuid.UUID Col10 *gtime.Time + Col11 decimal.Decimal + Col12 *decimal.Decimal } insertData := []*testNilTime{} + money := decimal.NewFromFloat(1.12) + strMoney, _ := decimal.NewFromString("99999.999") for i := 0; i < 10000; i++ { insertData = append(insertData, &testNilTime{ Col4: "Inc.", @@ -447,6 +455,8 @@ func TestDriverClickhouse_NilTime(t *testing.T) { map[string]string{"key": "value"}, map[string]string{"key": "value"}, }}, + Col11: money, + Col12: &strMoney, }) } _, err := connect.Model("data_type").Data(insertData).Insert() @@ -454,6 +464,13 @@ func TestDriverClickhouse_NilTime(t *testing.T) { count, err := connect.Model("data_type").Where("Col4", "Inc.").Count() gtest.AssertNil(err) gtest.AssertEQ(count, 10000) + + data, err := connect.Model("data_type").Where("Col4", "Inc.").One() + gtest.AssertNil(err) + gtest.AssertNE(data, nil) + g.Dump(data) + gtest.AssertEQ(data["Col11"].String(), "1.12") + gtest.AssertEQ(data["Col12"].String(), "99999.99") } func TestDriverClickhouse_BatchInsert(t *testing.T) { @@ -503,21 +520,6 @@ func TestDriverClickhouse_Open(t *testing.T) { gtest.AssertNil(db.PingMaster()) } -func TestDriverClickhouse_ReplaceConfig(t *testing.T) { - db := &Driver{} - // parse link's name set to config - c1 := &gdb.ConfigNode{} - c1.Link = "clickhouse://default@127.0.0.1:9000,127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60" - _, _ = db.Open(c1) - gtest.AssertEQ(c1.Name, "default") - // replace link's name from config - c2 := &gdb.ConfigNode{} - c2.Name = "clickhouseJohn" - c2.Link = "clickhouse://default@127.0.0.1:9000,127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60" - _, _ = db.Open(c2) - gtest.AssertEQ(strings.Contains(c2.Link, "clickhouseJohn"), true) -} - func TestDriverClickhouse_TableFields(t *testing.T) { connect := clickhouseConfigDB() gtest.AssertNil(createClickhouseExampleTable(connect)) @@ -537,6 +539,8 @@ func TestDriverClickhouse_TableFields(t *testing.T) { "Col8": {8, "Col8", "DateTime", false, "", "", "", "列8"}, "Col9": {9, "Col9", "UUID", false, "", "", "", "列9"}, "Col10": {10, "Col10", "DateTime", false, "", "", "", "列10"}, + "Col11": {11, "Col11", "Decimal(9, 2)", false, "", "", "", "列11"}, + "Col12": {12, "Col12", "Decimal(9, 2)", false, "", "", "", "列12"}, } for k, v := range result { _, ok := dataTypeTable[k] diff --git a/contrib/drivers/clickhouse/go.mod b/contrib/drivers/clickhouse/go.mod index c5a6d545e..58dffb2f9 100644 --- a/contrib/drivers/clickhouse/go.mod +++ b/contrib/drivers/clickhouse/go.mod @@ -6,6 +6,7 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.0.15 github.com/gogf/gf/v2 v2.0.0 github.com/google/uuid v1.3.0 + github.com/shopspring/decimal v1.3.1 ) replace ( diff --git a/contrib/drivers/dm/dm.go b/contrib/drivers/dm/dm.go new file mode 100644 index 000000000..793311ab8 --- /dev/null +++ b/contrib/drivers/dm/dm.go @@ -0,0 +1,371 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package dm implements gdb.Driver, which supports operations for database DM. +package dm + +import ( + "context" + "database/sql" + "fmt" + "net/url" + "reflect" + "strconv" + "strings" + + _ "gitee.com/chunanyong/dm" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gutil" +) + +type Driver struct { + *gdb.Core +} + +func init() { + var ( + err error + driverObj = New() + driverNames = g.SliceStr{"dm"} + ) + for _, driverName := range driverNames { + if err = gdb.Register(driverName, driverObj); err != nil { + panic(err) + } + } +} + +func New() gdb.Driver { + return &Driver{} +} + +func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { + return &Driver{ + Core: core, + }, nil +} + +func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { + var ( + source string + underlyingDriverName = "dm" + ) + if config.Name == "" { + return nil, fmt.Errorf( + `dm.Open failed for driver "%s" without DB Name`, underlyingDriverName, + ) + } + // Data Source Name of DM8: + // dm://userName:password@ip:port/dbname + source = fmt.Sprintf( + "dm://%s:%s@%s:%s/%s?charset=%s", + config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset, + ) + // Demo of timezone setting: + // &loc=Asia/Shanghai + if config.Timezone != "" { + source = fmt.Sprintf("%s&loc%s", source, url.QueryEscape(config.Timezone)) + } + if config.Extra != "" { + source = fmt.Sprintf("%s&%s", source, config.Extra) + } + + if db, err = sql.Open(underlyingDriverName, source); err != nil { + err = gerror.WrapCodef( + gcode.CodeDbOperationError, err, + `dm.Open failed for driver "%s" by source "%s"`, underlyingDriverName, source, + ) + return nil, err + } + return +} + +func (d *Driver) GetChars() (charLeft string, charRight string) { + return `"`, `"` +} + +func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, err error) { + var result gdb.Result + // When schema is empty, return the default link + link, err := d.SlaveLink(schema...) + if err != nil { + return nil, err + } + // The link has been distinguished and no longer needs to judge the owner + result, err = d.DoSelect( + ctx, link, `SELECT * FROM ALL_TABLES`, + ) + if err != nil { + return + } + for _, m := range result { + if v, ok := m["IOT_NAME"]; ok { + tables = append(tables, v.String()) + } + } + return +} + +func (d *Driver) TableFields( + ctx context.Context, table string, schema ...string, +) (fields map[string]*gdb.TableField, err error) { + var ( + result gdb.Result + link gdb.Link + // When no schema is specified, the configuration item is returned by default + usedSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) + ) + // When usedSchema is empty, return the default link + if link, err = d.SlaveLink(usedSchema); err != nil { + return nil, err + } + // The link has been distinguished and no longer needs to judge the owner + result, err = d.DoSelect( + ctx, link, + fmt.Sprintf( + `SELECT * FROM ALL_TAB_COLUMNS WHERE Table_Name= '%s'`, + strings.ToUpper(table), + ), + ) + if err != nil { + return nil, err + } + fields = make(map[string]*gdb.TableField) + for _, m := range result { + // m[NULLABLE] returns "N" "Y" + // "N" means not null + // "Y" means could be null + var nullable bool + if m["NULLABLE"].String() != "N" { + nullable = true + } + fields[m["COLUMN_NAME"].String()] = &gdb.TableField{ + Index: m["COLUMN_ID"].Int(), + Name: m["COLUMN_NAME"].String(), + Type: m["DATA_TYPE"].String(), + Null: nullable, + Default: m["DATA_DEFAULT"].Val(), + // Key: m["Key"].String(), + // Extra: m["Extra"].String(), + // Comment: m["Comment"].String(), + } + } + return fields, nil +} + +// DoFilter deals with the sql string before commits it to underlying sql driver. +func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { + defer func() { + newSql, newArgs, err = d.Core.DoFilter(ctx, link, newSql, newArgs) + }() + // There should be no need to capitalize, because it has been done from field processing before + newSql, _ = gregex.ReplaceString(`["\n\t]`, "", sql) + // g.Dump("Driver.DoFilter()::newSql", newSql) + newArgs = args + // g.Dump("Driver.DoFilter()::newArgs", newArgs) + return +} + +func (d *Driver) DoInsert( + ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption, +) (result sql.Result, err error) { + switch option.InsertOption { + case gdb.InsertOptionReplace: + // TODO:: Should be Supported + return nil, gerror.NewCode( + gcode.CodeNotSupported, `Replace operation is not supported by dm driver`, + ) + + case gdb.InsertOptionSave: + // This syntax currently only supports design tables whose primary key is ID. + listLength := len(list) + if listLength == 0 { + return nil, gerror.NewCode( + gcode.CodeInvalidRequest, `Save operation list is empty by dm driver`, + ) + } + var ( + keysSort []string + charL, charR = d.GetChars() + ) + // Column names need to be aligned in the syntax + for k := range list[0] { + keysSort = append(keysSort, k) + } + var char = struct { + charL string + charR string + valueCharL string + valueCharR string + duplicateKey string + keys []string + }{ + charL: charL, + charR: charR, + valueCharL: "'", + valueCharR: "'", + // TODO:: Need to dynamically set the primary key of the table + duplicateKey: "ID", + keys: keysSort, + } + + // insertKeys: Handle valid keys that need to be inserted and updated + // insertValues: Handle values that need to be inserted + // updateValues: Handle values that need to be updated + // queryValues: Handle only one insert with column name + insertKeys, insertValues, updateValues, queryValues := parseValue(list[0], char) + // unionValues: Handling values that need to be inserted and updated + unionValues := parseUnion(list[1:], char) + + batchResult := new(gdb.SqlResult) + // parseSql(): + // MERGE INTO {{table}} T1 + // USING ( SELECT {{queryValues}} FROM DUAL + // {{unionValues}} ) T2 + // ON (T1.{{duplicateKey}} = T2.{{duplicateKey}}) + // WHEN NOT MATCHED THEN + // INSERT {{insertKeys}} VALUES {{insertValues}} + // WHEN MATCHED THEN + // UPDATE SET {{updateValues}} + sqlStr := parseSql( + insertKeys, insertValues, updateValues, queryValues, unionValues, table, char.duplicateKey, + ) + r, err := d.DoExec(ctx, link, sqlStr) + if err != nil { + return r, err + } + if n, err := r.RowsAffected(); err != nil { + return r, err + } else { + batchResult.Result = r + batchResult.Affected += n + } + return batchResult, nil + } + return d.Core.DoInsert(ctx, link, table, list, option) +} + +func parseValue(listOne gdb.Map, char struct { + charL string + charR string + valueCharL string + valueCharR string + duplicateKey string + keys []string +}) (insertKeys []string, insertValues []string, updateValues []string, queryValues []string) { + for _, column := range char.keys { + if listOne[column] == nil { + // remove unassigned struct object + continue + } + insertKeys = append(insertKeys, char.charL+column+char.charR) + insertValues = append(insertValues, "T2."+char.charL+column+char.charR) + if column != char.duplicateKey { + updateValues = append( + updateValues, + fmt.Sprintf(`T1.%s = T2.%s`, char.charL+column+char.charR, char.charL+column+char.charR), + ) + } + + va := reflect.ValueOf(listOne[column]) + ty := reflect.TypeOf(listOne[column]) + saveValue := "" + switch ty.Kind() { + case reflect.String: + saveValue = va.String() + + case reflect.Int: + saveValue = strconv.FormatInt(va.Int(), 10) + + case reflect.Int64: + saveValue = strconv.FormatInt(va.Int(), 10) + + default: + // The fish has no chance getting here. + // Nothing to do. + } + queryValues = append( + queryValues, + fmt.Sprintf( + char.valueCharL+"%s"+char.valueCharR+" AS "+char.charL+"%s"+char.charR, + saveValue, column, + ), + ) + } + return +} + +func parseUnion(list gdb.List, char struct { + charL string + charR string + valueCharL string + valueCharR string + duplicateKey string + keys []string +}) (unionValues []string) { + for _, mapper := range list { + var saveValue []string + for _, column := range char.keys { + if mapper[column] == nil { + continue + } + va := reflect.ValueOf(mapper[column]) + ty := reflect.TypeOf(mapper[column]) + switch ty.Kind() { + case reflect.String: + saveValue = append(saveValue, char.valueCharL+va.String()+char.valueCharR) + + case reflect.Int: + saveValue = append(saveValue, strconv.FormatInt(va.Int(), 10)) + + case reflect.Int64: + saveValue = append(saveValue, strconv.FormatInt(va.Int(), 10)) + + default: + // The fish has no chance getting here. + // Nothing to do. + } + } + unionValues = append( + unionValues, + fmt.Sprintf(`UNION ALL SELECT %s FROM DUAL`, strings.Join(saveValue, ",")), + ) + } + return +} + +func parseSql( + insertKeys, insertValues, updateValues, queryValues, unionValues []string, table, duplicateKey string, +) (sqlStr string) { + var ( + queryValueStr = strings.Join(queryValues, ",") + unionValueStr = strings.Join(unionValues, " ") + insertKeyStr = strings.Join(insertKeys, ",") + insertValueStr = strings.Join(insertValues, ",") + updateValueStr = strings.Join(updateValues, ",") + pattern = gstr.Trim(` +MERGE INTO %s T1 USING (SELECT %s FROM DUAL %s) T2 ON %s +WHEN NOT MATCHED +THEN +INSERT(%s) VALUES (%s) +WHEN MATCHED +THEN +UPDATE SET %s; +COMMIT; +`) + ) + return fmt.Sprintf( + pattern, + table, queryValueStr, unionValueStr, + fmt.Sprintf("(T1.%s = T2.%s)", duplicateKey, duplicateKey), + insertKeyStr, insertValueStr, updateValueStr, + ) +} diff --git a/contrib/drivers/dm/dm_init_test.go b/contrib/drivers/dm/dm_init_test.go new file mode 100644 index 000000000..ec5d3fe69 --- /dev/null +++ b/contrib/drivers/dm/dm_init_test.go @@ -0,0 +1,186 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package dm_test + +import ( + "context" + "fmt" + "strings" + "time" + + _ "gitee.com/chunanyong/dm" + "github.com/gogf/gf/v2/container/garray" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/test/gtest" +) + +var ( + db gdb.DB + dblink gdb.DB + dbErr gdb.DB + ctx context.Context +) + +const ( + TableSize = 10 + + // TableName = "inf_group" + // TableNamePrefix = "t_" + // TestSchema = "SYSDBADP" +) + +const ( + TestDbIP = "127.0.0.1" + TestDbPort = "5236" + TestDbUser = "SYSDBA" + TestDbPass = "SYSDBA001" + TestDbName = "SYSDBA" + TestDbType = "dm" + TestCharset = "utf8" +) + +func init() { + node := gdb.ConfigNode{ + Host: TestDbIP, + Port: TestDbPort, + User: TestDbUser, + Pass: TestDbPass, + Name: TestDbName, + Type: TestDbType, + Role: "master", + Charset: TestCharset, + Weight: 1, + MaxIdleConnCount: 10, + MaxOpenConnCount: 10, + CreatedAt: "created_time", + UpdatedAt: "updated_time", + } + + nodeLink := gdb.ConfigNode{ + Type: TestDbType, + Name: TestDbName, + Link: fmt.Sprintf( + "dm:%s:%s@tcp(%s:%s)/%s?charset=%s", + TestDbUser, TestDbPass, TestDbIP, TestDbPort, TestDbName, TestCharset, + ), + } + + nodeErr := gdb.ConfigNode{ + Host: TestDbIP, + Port: TestDbPort, + User: TestDbUser, + Pass: "1234", + Name: TestDbName, + Type: TestDbType, + Role: "master", + Charset: TestCharset, + Weight: 1, + } + + gdb.AddConfigNode(gdb.DefaultGroupName, node) + if r, err := gdb.New(node); err != nil { + gtest.Fatal(err) + } else { + db = r + } + + gdb.AddConfigNode("dblink", nodeLink) + if r, err := gdb.New(nodeLink); err != nil { + gtest.Fatal(err) + } else { + dblink = r + } + + gdb.AddConfigNode("dbErr", nodeErr) + if r, err := gdb.New(nodeErr); err != nil { + gtest.Fatal(err) + } else { + dbErr = r + } + + ctx = context.Background() +} + +func createTable(table ...string) (name string) { + if len(table) > 0 { + name = table[0] + } else { + name = fmt.Sprintf("random_%d", gtime.Timestamp()) + } + + dropTable(name) + + if _, err := db.Exec(ctx, fmt.Sprintf(` + CREATE TABLE "%s" +( +"ID" BIGINT NOT NULL, +"ACCOUNT_NAME" VARCHAR(128) DEFAULT '' NOT NULL, +"PWD_RESET" TINYINT DEFAULT 0 NOT NULL, +"ENABLED" INT DEFAULT 1 NOT NULL, +"DELETED" INT DEFAULT 0 NOT NULL, +"CREATED_BY" VARCHAR(32) DEFAULT '' NOT NULL, +"CREATED_TIME" TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP() NOT NULL, +"UPDATED_BY" VARCHAR(32) DEFAULT '' NOT NULL, +"UPDATED_TIME" TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP() NOT NULL, +NOT CLUSTER PRIMARY KEY("ID")) STORAGE(ON "MAIN", CLUSTERBTR) ; + `, name)); err != nil { + gtest.Fatal(err) + } + + return +} + +type User struct { + ID int64 `orm:"id"` + AccountName string `orm:"account_name"` + PwdReset int64 `orm:"pwd_reset"` + Enabled int64 `orm:"enabled"` + Deleted int64 `orm:"deleted"` + CreatedBy string `orm:"created_by"` + CreatedTime time.Time `orm:"created_time"` + UpdatedBy string `orm:"updated_by"` + UpdatedTime time.Time `orm:"updated_time"` +} + +func createInitTable(table ...string) (name string) { + name = createTable(table...) + array := garray.New(true) + for i := 1; i <= TableSize; i++ { + array.Append(g.Map{ + "id": i, + "account_name": fmt.Sprintf(`name_%d`, i), + "pwd_reset": 0, + "create_time": gtime.Now().String(), + }) + } + result, err := db.Schema(TestDbName).Insert(context.Background(), name, array.Slice()) + gtest.Assert(err, nil) + + n, e := result.RowsAffected() + gtest.Assert(e, nil) + gtest.Assert(n, TableSize) + return +} + +func dropTable(table string) { + count, err := db.GetCount( + ctx, + "SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME = ?", strings.ToUpper(table), + ) + if err != nil { + gtest.Fatal(err) + } + + if count == 0 { + return + } + if _, err := db.Exec(ctx, fmt.Sprintf("DROP TABLE %s", table)); err != nil { + gtest.Fatal(err) + } +} diff --git a/contrib/drivers/dm/dm_z_basic_test.go b/contrib/drivers/dm/dm_z_basic_test.go new file mode 100644 index 000000000..5f4bc0dd2 --- /dev/null +++ b/contrib/drivers/dm/dm_z_basic_test.go @@ -0,0 +1,584 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package dm_test + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_DB_Ping(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err1 := dblink.PingMaster() + err2 := dblink.PingSlave() + t.Assert(err1, nil) + t.Assert(err2, nil) + }) +} + +func TestTables(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + tables := []string{"A_tables", "A_tables2"} + + for _, v := range tables { + createInitTable(v) + // createTable(v) + } + result, err := db.Tables(ctx) + gtest.Assert(err, nil) + + for i := 0; i < len(tables); i++ { + find := false + for j := 0; j < len(result); j++ { + if strings.ToUpper(tables[i]) == result[j] { + find = true + break + } + } + gtest.AssertEQ(find, true) + } + + result, err = dblink.Tables(ctx) + gtest.Assert(err, nil) + for i := 0; i < len(tables); i++ { + find := false + for j := 0; j < len(result); j++ { + if strings.ToUpper(tables[i]) == result[j] { + find = true + break + } + } + gtest.AssertEQ(find, true) + } + }) +} + +func TestTableFields(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + tables := "A_tables" + var expect = map[string][]interface{}{ + "ID": {"BIGINT", false}, + "ACCOUNT_NAME": {"VARCHAR", false}, + "PWD_RESET": {"TINYINT", false}, + "DELETED": {"INT", false}, + "CREATED_TIME": {"TIMESTAMP", false}, + } + + _, err := dbErr.TableFields(ctx, "Fields") + gtest.AssertNE(err, nil) + + res, err := db.TableFields(ctx, tables) + gtest.Assert(err, nil) + + for k, v := range expect { + _, ok := res[k] + gtest.AssertEQ(ok, true) + + gtest.AssertEQ(res[k].Name, k) + gtest.Assert(res[k].Type, v[0]) + gtest.Assert(res[k].Null, v[1]) + } + + }) + + gtest.C(t, func(t *gtest.T) { + _, err := db.TableFields(ctx, "t_user t_user2") + gtest.AssertNE(err, nil) + }) +} + +func Test_DB_Query(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + tableName := "A_tables" + // createTable(tableName) + _, err := db.Query(ctx, fmt.Sprintf("SELECT * from %s", tableName)) + t.AssertNil(err) + + resTwo := make([]User, 0) + err = db.Model(tableName).Scan(&resTwo) + t.AssertNil(err) + + resThree := make([]User, 0) + model := db.Model(tableName) + model.Where("id", g.Slice{1, 2, 3, 4}) + // model.Where("account_name like ?", "%"+"list"+"%") + model.Where("deleted", 0).Order("pwd_reset desc") + _, err = model.Count() + t.AssertNil(err) + err = model.Page(2, 2).Scan(&resThree) + t.AssertNil(err) + }) +} + +func TestModelSave(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // createTable("A_tables") + data := []User{ + { + ID: 100, + AccountName: "user_100", + CreatedTime: time.Now(), + }, + } + _, err := db.Model("A_tables").Data(data).Save() + gtest.Assert(err, nil) + + data2 := []User{ + { + ID: 101, + AccountName: "user_101", + }, + } + _, err = db.Model("A_tables").Data(&data2).Save() + gtest.Assert(err, nil) + + data3 := []User{ + { + ID: 10, + AccountName: "user_10", + PwdReset: 10, + }, + } + _, err = db.Model("A_tables").Save(data3) + gtest.Assert(err, nil) + + data4 := []User{ + { + ID: 9, + AccountName: "user_9", + CreatedTime: time.Now(), + }, + } + _, err = db.Model("A_tables").Save(&data4) + gtest.Assert(err, nil) + + // TODO:: Should be Supported 'Replace' Operation + // _, err = db.Schema(TestDbName).Replace(ctx, "DoInsert", data, 10) + // gtest.Assert(err, nil) + }) +} + +func TestModelInsert(t *testing.T) { + // g.Model.insert not lost default not null coloumn + gtest.C(t, func(t *gtest.T) { + // createTable("A_tables") + i := 200 + data := User{ + ID: int64(i), + AccountName: fmt.Sprintf(`A%dtwo`, i), + PwdReset: 0, + // CreatedTime: time.Now(), + UpdatedTime: time.Now(), + } + // _, err := db.Schema(TestDbName).Model("A_tables").Data(data).Insert() + _, err := db.Model("A_tables").Insert(&data) + gtest.Assert(err, nil) + }) + + gtest.C(t, func(t *gtest.T) { + // createTable("A_tables") + i := 201 + data := User{ + ID: int64(i), + AccountName: fmt.Sprintf(`A%dtwoONE`, i), + PwdReset: 1, + CreatedTime: time.Now(), + // UpdatedTime: time.Now(), + } + // _, err := db.Schema(TestDbName).Model("A_tables").Data(data).Insert() + _, err := db.Model("A_tables").Data(&data).Insert() + gtest.Assert(err, nil) + }) +} + +func TestDBInsert(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // createTable("A_tables") + i := 300 + data := g.Map{ + "ID": i, + "ACCOUNT_NAME": fmt.Sprintf(`A%dthress`, i), + "PWD_RESET": 3, + } + _, err := db.Insert(ctx, "A_tables", &data) + gtest.Assert(err, nil) + }) +} + +func Test_DB_Exec(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + _, err := db.Exec(ctx, "SELECT ? from dual", 1) + t.AssertNil(err) + + _, err = db.Exec(ctx, "ERROR") + t.AssertNE(err, nil) + }) +} + +func Test_DB_Insert(t *testing.T) { + // table := createTable() + // defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + // normal map + _, err := db.Insert(ctx, "A_tables", g.Map{ + "ID": 1000, + "ACCOUNT_NAME": "map1", + "CREATED_TIME": gtime.Now().String(), + }) + t.AssertNil(err) + + result, err := db.Insert(ctx, "A_tables", g.Map{ + "ID": "2000", + "ACCOUNT_NAME": "map2", + "CREATED_TIME": gtime.Now(), + }) + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + + result, err = db.Insert(ctx, "A_tables", g.Map{ + "ID": 3000, + "ACCOUNT_NAME": "map3", + // "CREATED_TIME": gtime.Now().String(), + }) + t.AssertNil(err) + n, _ = result.RowsAffected() + t.Assert(n, 1) + + // struct + result, err = db.Insert(ctx, "A_tables", User{ + ID: 4000, + AccountName: "struct_4", + // CreatedTime: timeStr, + // UpdatedTime: timeStr, + }) + t.AssertNil(err) + n, _ = result.RowsAffected() + t.Assert(n, 1) + + ones, err := db.Model("A_tables").Where("ID", 4000).All() + t.AssertNil(err) + t.Assert(ones[0]["ID"].Int(), 4000) + t.Assert(ones[0]["ACCOUNT_NAME"].String(), "struct_4") + // TODO Question2 + // this is DM bug. + // t.Assert(one["CREATED_TIME"].GTime().String(), timeStr) + + // *struct + timeStr := time.Now() + result, err = db.Insert(ctx, "A_tables", &User{ + ID: 5000, + AccountName: "struct_5", + CreatedTime: timeStr, + // UpdatedTime: timeStr, + }) + t.AssertNil(err) + n, _ = result.RowsAffected() + t.Assert(n, 1) + + one, err := db.Model("A_tables").Where("ID", 5000).One() + t.AssertNil(err) + t.Assert(one["ID"].Int(), 5000) + t.Assert(one["ACCOUNT_NAME"].String(), "struct_5") + + // batch with Insert + r, err := db.Insert(ctx, "A_tables", g.Slice{ + g.Map{ + "ID": 6000, + "ACCOUNT_NAME": "t6000", + }, + g.Map{ + "ID": 6001, + "ACCOUNT_NAME": "t6001", + }, + }) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + one, err = db.Model("A_tables").Where("ID", 6000).One() + t.AssertNil(err) + t.Assert(one["ID"].Int(), 6000) + t.Assert(one["ACCOUNT_NAME"].String(), "t6000") + }) +} + +func Test_DB_BatchInsert(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + table := "A_tables" + r, err := db.Insert(ctx, table, g.List{ + { + "ID": 400, + "ACCOUNT_NAME": "list_400", + // "CREATE_TIME": gtime.Now().String(), + }, + { + "ID": 401, + "ACCOUNT_NAME": "list_401", + "CREATE_TIME": gtime.Now().String(), + }, + }, 1) + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 2) + }) + + gtest.C(t, func(t *gtest.T) { + table := "A_tables" + // table := createTable() + // defer dropTable(table) + // []interface{} + r, err := db.Insert(ctx, table, g.Slice{ + g.Map{ + "ID": 500, + "ACCOUNT_NAME": "500_batch_500", + "CREATE_TIME": gtime.Now().String(), + }, + g.Map{ + "ID": 501, + "ACCOUNT_NAME": "501_batch_501", + // "CREATE_TIME": gtime.Now().String(), + }, + }, 1) + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 2) + }) + + // batch insert map + gtest.C(t, func(t *gtest.T) { + table := "A_tables" + // table := createTable() + // defer dropTable(table) + result, err := db.Insert(ctx, table, g.Map{ + "ID": 600, + "ACCOUNT_NAME": "600_batch_600", + "CREATE_TIME": gtime.Now().String(), + }) + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + }) +} + +func Test_DB_BatchInsert_Struct(t *testing.T) { + // batch insert struct + gtest.C(t, func(t *gtest.T) { + table := "A_tables" + // table := createTable() + // defer dropTable(table) + user := &User{ + ID: 700, + AccountName: "BatchInsert_Struct_700", + // CreatedTime: time.Now(), + } + result, err := db.Model(table).Insert(user) + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + }) +} + +func Test_DB_Update(t *testing.T) { + table := "A_tables" + // table := createInitTable() + + gtest.C(t, func(t *gtest.T) { + result, err := db.Update(ctx, table, "pwd_reset=7", "id=700") + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + + one, err := db.Model(table).Where("ID", 700).One() + t.AssertNil(err) + t.Assert(one["ID"].Int(), 700) + t.Assert(one["ACCOUNT_NAME"].String(), "BatchInsert_Struct_700") + t.Assert(one["PWD_RESET"].String(), "7") + }) +} + +func Test_DB_GetAll(t *testing.T) { + table := "A_tables" + // table := createInitTable() + // defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1) + t.AssertNil(err) + t.Assert(len(result), 1) + t.Assert(result[0]["ID"].Int(), 1) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), g.Slice{1}) + t.AssertNil(err) + t.Assert(len(result), 1) + t.Assert(result[0]["ID"].Int(), 1) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id in(?)", table), g.Slice{1, 2, 3}) + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["ID"].Int(), 1) + t.Assert(result[1]["ID"].Int(), 2) + t.Assert(result[2]["ID"].Int(), 3) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3}) + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["ID"].Int(), 1) + t.Assert(result[1]["ID"].Int(), 2) + t.Assert(result[2]["ID"].Int(), 3) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3}...) + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["ID"].Int(), 1) + t.Assert(result[1]["ID"].Int(), 2) + t.Assert(result[2]["ID"].Int(), 3) + }) + gtest.C(t, func(t *gtest.T) { + result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id>=? AND id <=?", table), g.Slice{1, 3}) + t.AssertNil(err) + t.Assert(len(result), 3) + t.Assert(result[0]["ID"].Int(), 1) + t.Assert(result[1]["ID"].Int(), 2) + t.Assert(result[2]["ID"].Int(), 3) + }) +} + +func Test_DB_GetOne(t *testing.T) { + // table := createInitTable() + table := "A_tables" + gtest.C(t, func(t *gtest.T) { + record, err := db.GetOne(ctx, fmt.Sprintf("SELECT * FROM %s WHERE account_name=?", table), "struct_4") + t.AssertNil(err) + t.Assert(record["ACCOUNT_NAME"].String(), "struct_4") + }) +} + +func Test_DB_GetValue(t *testing.T) { + table := "A_tables" + // table := createInitTable() + // defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + value, err := db.GetValue(ctx, fmt.Sprintf("SELECT id FROM %s WHERE account_name=?", table), "map2") + t.AssertNil(err) + t.Assert(value.Int(), 2000) + }) +} + +func Test_DB_GetCount(t *testing.T) { + table := "A_tables" + // table := createInitTable() + // defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + count, err := db.GetCount(ctx, fmt.Sprintf("SELECT * FROM %s", table)) + t.AssertNil(err) + t.Assert(count, 28) + }) +} + +func Test_DB_GetStruct(t *testing.T) { + table := "A_tables" + // table := createInitTable() + // defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + user := new(User) + err := db.GetScan(ctx, user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) + t.AssertNil(err) + t.Assert(user.AccountName, "name_3") + }) + gtest.C(t, func(t *gtest.T) { + user := new(User) + err := db.GetScan(ctx, user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 200) + t.AssertNil(err) + t.Assert(user.AccountName, "A200two") + }) +} + +func Test_DB_GetStructs(t *testing.T) { + table := "A_tables" + // table := createInitTable() + // defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.GetScan(ctx, &users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 4000) + t.AssertNil(err) + t.Assert(users[0].ID, 5000) + t.Assert(users[1].ID, 6000) + t.Assert(users[2].ID, 6001) + t.Assert(users[0].AccountName, "struct_5") + t.Assert(users[1].AccountName, "t6000") + t.Assert(users[2].AccountName, "t6001") + }) +} + +func Test_DB_GetScan(t *testing.T) { + table := "A_tables" + // table := createInitTable() + // defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + user := new(User) + err := db.GetScan(ctx, user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) + t.AssertNil(err) + t.Assert(user.AccountName, "name_3") + }) + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.GetScan(ctx, &user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3) + t.AssertNil(err) + t.Assert(user.AccountName, "name_3") + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.GetScan(ctx, &users, fmt.Sprintf("SELECT * FROM %s WHERE id ../../../ + +require ( + gitee.com/chunanyong/dm v1.8.6 + github.com/gogf/gf/v2 v2.0.0 +) diff --git a/contrib/drivers/dm/go.sum b/contrib/drivers/dm/go.sum new file mode 100644 index 000000000..ce663d0c0 --- /dev/null +++ b/contrib/drivers/dm/go.sum @@ -0,0 +1,171 @@ +gitee.com/chunanyong/dm v1.8.6 h1:5UnOCW1f2+LYiSQvuHiloS6OTMnZAtjRQ4woi9i6QY4= +gitee.com/chunanyong/dm v1.8.6/go.mod h1:EPRJnuPFgbyOFgJ0TRYCTGzhq+ZT4wdyaj/GW/LLcNg= +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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.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/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= +github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= +go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= +go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/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/sync v0.0.0-20180314180146-1d60e4601c6f/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-20201020160332-67f06af15bc9/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y= +golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/contrib/drivers/mssql/mssql.go b/contrib/drivers/mssql/mssql.go index 4b97d39a4..a107df499 100644 --- a/contrib/drivers/mssql/mssql.go +++ b/contrib/drivers/mssql/mssql.go @@ -3,13 +3,13 @@ // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. + +// Package mssql implements gdb.Driver, which supports operations for database MSSql. // // Note: // 1. It needs manually import: _ "github.com/denisenkom/go-mssqldb" // 2. It does not support Save/Replace features. // 3. It does not support LastInsertId. - -// Package mssql implements gdb.Driver, which supports operations for MSSql. package mssql import ( @@ -20,8 +20,8 @@ import ( "strings" _ "github.com/denisenkom/go-mssqldb" + "github.com/gogf/gf/v2/util/gutil" - "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" @@ -34,11 +34,6 @@ type Driver struct { *gdb.Core } -var ( - // tableFieldsMap caches the table information retrieved from database. - tableFieldsMap = gmap.New(true) -) - func init() { if err := gdb.Register(`mssql`, New()); err != nil { panic(err) @@ -65,6 +60,9 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { underlyingDriverName = "sqlserver" ) if config.Link != "" { + // ============================================================================ + // Deprecated from v2.2.0. + // ============================================================================ source = config.Link // Custom changing the schema in runtime. if config.Name != "" { @@ -75,6 +73,15 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { "user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable", config.User, config.Pass, config.Host, config.Port, config.Name, ) + if config.Extra != "" { + var extraMap map[string]interface{} + if extraMap, err = gstr.Parse(config.Extra); err != nil { + return nil, err + } + for k, v := range extraMap { + source += fmt.Sprintf(`;%s=%s`, k, v) + } + } } if db, err = sql.Open(underlyingDriverName, source); err != nil { @@ -87,21 +94,6 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { return } -// FilteredLink retrieves and returns filtered `linkInfo` that can be using for -// logging or tracing purpose. -func (d *Driver) FilteredLink() string { - linkInfo := d.GetConfig().Link - if linkInfo == "" { - return "" - } - s, _ := gregex.ReplaceString( - `(.+);\s*password=(.+);\s*server=(.+)`, - `$1;password=xxx;server=$3`, - d.GetConfig().Link, - ) - return s -} - // GetChars returns the security char for this type of database. func (d *Driver) GetChars() (charLeft string, charRight string) { return `"`, `"` @@ -227,7 +219,9 @@ func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, return nil, err } - result, err = d.DoSelect(ctx, link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`) + result, err = d.DoSelect( + ctx, link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`, + ) if err != nil { return } @@ -243,26 +237,15 @@ func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, // // Also see DriverMysql.TableFields. func (d *Driver) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*gdb.TableField, err error) { - charL, charR := d.GetChars() - table = gstr.Trim(table, charL+charR) - if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") + var ( + result gdb.Result + link gdb.Link + useSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) + ) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil, err } - useSchema := d.GetSchema() - if len(schema) > 0 && schema[0] != "" { - useSchema = schema[0] - } - v := tableFieldsMap.GetOrSetFuncLock( - fmt.Sprintf(`mssql_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), - func() interface{} { - var ( - result gdb.Result - link gdb.Link - ) - if link, err = d.SlaveLink(useSchema); err != nil { - return nil - } - structureSql := fmt.Sprintf(` + structureSql := fmt.Sprintf(` SELECT a.name Field, CASE b.name @@ -290,34 +273,28 @@ LEFT JOIN sys.extended_properties g ON a.id=g.major_id AND a.colid=g.minor_id LEFT JOIN sys.extended_properties f ON d.id=f.major_id AND f.minor_id =0 WHERE d.name='%s' ORDER BY a.id,a.colorder`, - table, - ) - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.DoSelect(ctx, link, structureSql) - if err != nil { - return nil - } - fields = make(map[string]*gdb.TableField) - for i, m := range result { - - fields[m["Field"].String()] = &gdb.TableField{ - Index: i, - Name: m["Field"].String(), - Type: m["Type"].String(), - Null: m["Null"].Bool(), - Key: m["Key"].String(), - Default: m["Default"].Val(), - Extra: m["Extra"].String(), - Comment: m["Comment"].String(), - } - } - return fields - }, + table, ) - if v != nil { - fields = v.(map[string]*gdb.TableField) + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + result, err = d.DoSelect(ctx, link, structureSql) + if err != nil { + return nil, err } - return + fields = make(map[string]*gdb.TableField) + for i, m := range result { + + fields[m["Field"].String()] = &gdb.TableField{ + Index: i, + Name: m["Field"].String(), + Type: m["Type"].String(), + Null: m["Null"].Bool(), + Key: m["Key"].String(), + Default: m["Default"].Val(), + Extra: m["Extra"].String(), + Comment: m["Comment"].String(), + } + } + return fields, nil } // DoInsert is not supported in mssql. diff --git a/contrib/drivers/mssql/mssql_z_basic_test.go b/contrib/drivers/mssql/mssql_z_basic_test.go index 5d6dd410e..3f151c6a8 100644 --- a/contrib/drivers/mssql/mssql_z_basic_test.go +++ b/contrib/drivers/mssql/mssql_z_basic_test.go @@ -9,13 +9,14 @@ package mssql_test import ( "context" "fmt" + "testing" + "time" + "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/encoding/gxml" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/test/gtest" - "testing" - "time" ) func TestTables(t *testing.T) { @@ -108,25 +109,6 @@ func TestTableFields(t *testing.T) { }) } -func TestFilteredLink(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - s := db.FilteredLink() - gtest.AssertEQ(s, "") - }) - - gtest.C(t, func(t *gtest.T) { - _, err := dblink.Query(ctx, "select 1") - gtest.Assert(err, nil) - - s := dblink.FilteredLink() - gtest.AssertNE(s, nil) - }) - - gtest.C(t, func(t *gtest.T) { - _, err := dbErr.Query(ctx, "select 1") - gtest.AssertNE(err, nil) - }) -} func TestDoInsert(t *testing.T) { gtest.C(t, func(t *gtest.T) { createTable("t_user") diff --git a/contrib/drivers/mysql/mysql.go b/contrib/drivers/mysql/mysql.go index 31343e05e..622d227cf 100644 --- a/contrib/drivers/mysql/mysql.go +++ b/contrib/drivers/mysql/mysql.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package mysql implements gdb.Driver, which supports operations for MySQL. +// Package mysql implements gdb.Driver, which supports operations for database MySQL. package mysql import ( @@ -15,25 +15,19 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gutil" - "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/text/gregex" - "github.com/gogf/gf/v2/text/gstr" ) -// DriverMysql is the driver for mysql database. -type DriverMysql struct { +// Driver is the driver for mysql database. +type Driver struct { *gdb.Core } -var ( - // tableFieldsMap caches the table information retrieved from database. - tableFieldsMap = gmap.New(true) -) - func init() { var ( err error @@ -49,26 +43,29 @@ func init() { // New create and returns a driver that implements gdb.Driver, which supports operations for MySQL. func New() gdb.Driver { - return &DriverMysql{} + return &Driver{} } // New creates and returns a database object for mysql. // It implements the interface of gdb.Driver for extra database driver installation. -func (d *DriverMysql) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { - return &DriverMysql{ +func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { + return &Driver{ Core: core, }, nil } // Open creates and returns an underlying sql.DB object for mysql. // Note that it converts time.Time argument to local timezone in default. -func (d *DriverMysql) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { +func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { var ( source string underlyingDriverName = "mysql" ) // [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] if config.Link != "" { + // ============================================================================ + // Deprecated from v2.2.0. + // ============================================================================ source = config.Link // Custom changing the schema in runtime. if config.Name != "" { @@ -76,12 +73,15 @@ func (d *DriverMysql) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { } } else { source = fmt.Sprintf( - "%s:%s@tcp(%s:%s)/%s?charset=%s", - config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset, + "%s:%s@%s(%s:%s)/%s?charset=%s", + config.User, config.Pass, config.Protocol, config.Host, config.Port, config.Name, config.Charset, ) if config.Timezone != "" { source = fmt.Sprintf("%s&loc=%s", source, url.QueryEscape(config.Timezone)) } + if config.Extra != "" { + source = fmt.Sprintf("%s&%s", source, config.Extra) + } } if db, err = sql.Open(underlyingDriverName, source); err != nil { err = gerror.WrapCodef( @@ -93,34 +93,19 @@ func (d *DriverMysql) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { return } -// FilteredLink retrieves and returns filtered `linkInfo` that can be using for -// logging or tracing purpose. -func (d *DriverMysql) FilteredLink() string { - linkInfo := d.GetConfig().Link - if linkInfo == "" { - return "" - } - s, _ := gregex.ReplaceString( - `(.+?):(.+)@tcp(.+)`, - `$1:xxx@tcp$3`, - linkInfo, - ) - return s -} - // GetChars returns the security char for this type of database. -func (d *DriverMysql) GetChars() (charLeft string, charRight string) { +func (d *Driver) GetChars() (charLeft string, charRight string) { return "`", "`" } // DoFilter handles the sql before posts it to database. -func (d *DriverMysql) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { +func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { return d.Core.DoFilter(ctx, link, sql, args) } // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. -func (d *DriverMysql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { +func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, err error) { var result gdb.Result link, err := d.SlaveLink(schema...) if err != nil { @@ -150,56 +135,36 @@ func (d *DriverMysql) Tables(ctx context.Context, schema ...string) (tables []st // // It's using cache feature to enhance the performance, which is never expired util the // process restarts. -func (d *DriverMysql) TableFields( +func (d *Driver) TableFields( ctx context.Context, table string, schema ...string, ) (fields map[string]*gdb.TableField, err error) { - charL, charR := d.GetChars() - table = gstr.Trim(table, charL+charR) - if gstr.Contains(table, " ") { - return nil, gerror.NewCode( - gcode.CodeInvalidParameter, - "function TableFields supports only single table operations", - ) - } - useSchema := d.GetSchema() - if len(schema) > 0 && schema[0] != "" { - useSchema = schema[0] - } - v := tableFieldsMap.GetOrSetFuncLock( - fmt.Sprintf(`mysql_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), - func() interface{} { - var ( - result gdb.Result - link gdb.Link - ) - if link, err = d.SlaveLink(useSchema); err != nil { - return nil - } - result, err = d.DoSelect( - ctx, link, - fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table)), - ) - if err != nil { - return nil - } - fields = make(map[string]*gdb.TableField) - for i, m := range result { - fields[m["Field"].String()] = &gdb.TableField{ - Index: i, - Name: m["Field"].String(), - Type: m["Type"].String(), - Null: m["Null"].Bool(), - Key: m["Key"].String(), - Default: m["Default"].Val(), - Extra: m["Extra"].String(), - Comment: m["Comment"].String(), - } - } - return fields - }, + var ( + result gdb.Result + link gdb.Link + useSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) ) - if v != nil { - fields = v.(map[string]*gdb.TableField) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil, err } - return + result, err = d.DoSelect( + ctx, link, + fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table)), + ) + if err != nil { + return nil, err + } + fields = make(map[string]*gdb.TableField) + for i, m := range result { + fields[m["Field"].String()] = &gdb.TableField{ + Index: i, + Name: m["Field"].String(), + Type: m["Type"].String(), + Null: m["Null"].Bool(), + Key: m["Key"].String(), + Default: m["Default"].Val(), + Extra: m["Extra"].String(), + Comment: m["Comment"].String(), + } + } + return fields, nil } diff --git a/contrib/drivers/mysql/mysql_basic_test.go b/contrib/drivers/mysql/mysql_basic_test.go index 8e50f7c47..bec900e5b 100644 --- a/contrib/drivers/mysql/mysql_basic_test.go +++ b/contrib/drivers/mysql/mysql_basic_test.go @@ -7,6 +7,7 @@ package mysql_test import ( + "context" "testing" "github.com/go-sql-driver/mysql" @@ -70,3 +71,29 @@ func Test_Func_FormatSqlWithArgs(t *testing.T) { t.Assert(s, "select * from table where id>=100 and sex=1") }) } + +func Test_Func_ToSQL(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + sql, err := gdb.ToSQL(ctx, func(ctx context.Context) error { + value, err := db.Ctx(ctx).Model(TableName).Fields("nickname").Where("id", 1).Value() + t.Assert(value, nil) + return err + }) + t.AssertNil(err) + t.Assert(sql, "SELECT `nickname` FROM `user` WHERE `id`=1 LIMIT 1") + }) +} + +func Test_Func_CatchSQL(t *testing.T) { + table := createInitTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + array, err := gdb.CatchSQL(ctx, func(ctx context.Context) error { + value, err := db.Ctx(ctx).Model(table).Fields("nickname").Where("id", 1).Value() + t.Assert(value, "name_1") + return err + }) + t.AssertNil(err) + t.AssertGE(len(array), 1) + }) +} diff --git a/contrib/drivers/mysql/mysql_core_test.go b/contrib/drivers/mysql/mysql_core_test.go index 6f55d4789..15c93f480 100644 --- a/contrib/drivers/mysql/mysql_core_test.go +++ b/contrib/drivers/mysql/mysql_core_test.go @@ -1602,3 +1602,39 @@ func Test_Types(t *testing.T) { t.Assert(obj.TinyInt, data["tinyint"]) }) } + +func Test_Core_ClearTableFields(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + fields, err := db.TableFields(ctx, table) + t.AssertNil(err) + t.Assert(len(fields), 5) + }) + gtest.C(t, func(t *gtest.T) { + err := db.GetCore().ClearTableFields(ctx, table) + t.AssertNil(err) + }) +} + +func Test_Core_ClearTableFieldsAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := db.GetCore().ClearTableFieldsAll(ctx) + t.AssertNil(err) + }) +} + +func Test_Core_ClearCache(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := db.GetCore().ClearCache(ctx, "") + t.AssertNil(err) + }) +} + +func Test_Core_ClearCacheAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := db.GetCore().ClearCacheAll(ctx) + t.AssertNil(err) + }) +} diff --git a/contrib/drivers/mysql/mysql_model_test.go b/contrib/drivers/mysql/mysql_model_test.go index 15468dfaf..170d960bf 100644 --- a/contrib/drivers/mysql/mysql_model_test.go +++ b/contrib/drivers/mysql/mysql_model_test.go @@ -984,7 +984,10 @@ func Test_Model_StructsWithOrmTag(t *testing.T) { dbInvalid.Model(table).Order("id asc").Scan(&users) //fmt.Println(buffer.String()) t.Assert( - gstr.Contains(buffer.String(), "SELECT `id`,`Passport`,`password`,`nick_name`,`create_time` FROM `user"), + gstr.Contains( + buffer.String(), + "SELECT `id`,`Passport`,`password`,`nick_name`,`create_time` FROM `user", + ), true, ) }) diff --git a/contrib/drivers/oracle/oracle.go b/contrib/drivers/oracle/oracle.go index 6065a0193..469d2818c 100644 --- a/contrib/drivers/oracle/oracle.go +++ b/contrib/drivers/oracle/oracle.go @@ -3,30 +3,31 @@ // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. + +// Package oracle implements gdb.Driver, which supports operations for database Oracle. // // Note: // 1. It needs manually import: _ "github.com/sijms/go-ora/v2" // 2. It does not support Save/Replace features. // 3. It does not support LastInsertId. - -// Package oracle implements gdb.Driver, which supports operations for Oracle. package oracle import ( "context" "database/sql" "fmt" + "strconv" + "strings" + + "github.com/gogf/gf/v2/util/gutil" gora "github.com/sijms/go-ora/v2" - "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" - "strconv" - "strings" ) // Driver is the driver for oracle database. @@ -34,11 +35,6 @@ type Driver struct { *gdb.Core } -var ( - // tableFieldsMap caches the table information retrieved from database. - tableFieldsMap = gmap.New(true) -) - func init() { if err := gdb.Register(`oracle`, New()); err != nil { panic(err) @@ -75,13 +71,27 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { } // [username:[password]@]host[:port][/service_name][?param1=value1&...¶mN=valueN] if config.Link != "" { + // ============================================================================ + // Deprecated from v2.2.0. + // ============================================================================ source = config.Link // Custom changing the schema in runtime. if config.Name != "" { source, _ = gregex.ReplaceString(`@(.+?)/([\w\.\-]+)+`, "@$1/"+config.Name, source) } } else { - source = gora.BuildUrl(config.Host, gconv.Int(config.Port), config.Name, config.User, config.Pass, options) + if config.Extra != "" { + var extraMap map[string]interface{} + if extraMap, err = gstr.Parse(config.Extra); err != nil { + return nil, err + } + for k, v := range extraMap { + options[k] = gconv.String(v) + } + } + source = gora.BuildUrl( + config.Host, gconv.Int(config.Port), config.Name, config.User, config.Pass, options, + ) } if db, err = sql.Open(underlyingDriverName, source); err != nil { @@ -94,21 +104,6 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { return } -// FilteredLink retrieves and returns filtered `linkInfo` that can be using for -// logging or tracing purpose. -func (d *Driver) FilteredLink() string { - linkInfo := d.GetConfig().Link - if linkInfo == "" { - return "" - } - s, _ := gregex.ReplaceString( - `(.+?)\s*:\s*(.+)\s*@\s*(.+)\s*:\s*(\d+)\s*/\s*(.+)`, - `$1:xxx@$3:$4/$5`, - linkInfo, - ) - return s -} - // GetChars returns the security char for this type of database. func (d *Driver) GetChars() (charLeft string, charRight string) { return `"`, `"` @@ -219,25 +214,11 @@ func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, func (d *Driver) TableFields( ctx context.Context, table string, schema ...string, ) (fields map[string]*gdb.TableField, err error) { - charL, charR := d.GetChars() - table = gstr.Trim(table, charL+charR) - if gstr.Contains(table, " ") { - return nil, gerror.NewCode( - gcode.CodeInvalidParameter, - "function TableFields supports only single table operations", - ) - } - useSchema := d.GetSchema() - if len(schema) > 0 && schema[0] != "" { - useSchema = schema[0] - } - v := tableFieldsMap.GetOrSetFuncLock( - fmt.Sprintf(`oracle_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), - func() interface{} { - var ( - result gdb.Result - link gdb.Link - structureSql = fmt.Sprintf(` + var ( + result gdb.Result + link gdb.Link + useSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) + structureSql = fmt.Sprintf(` SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE @@ -245,38 +226,32 @@ SELECT WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')' ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE,NULLABLE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, - strings.ToUpper(table), - ) - ) - if link, err = d.SlaveLink(useSchema); err != nil { - return nil - } - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.DoSelect(ctx, link, structureSql) - if err != nil { - return nil - } - fields = make(map[string]*gdb.TableField) - for i, m := range result { - isNull := false - if m["NULLABLE"].String() == "Y" { - isNull = true - } - - fields[m["FIELD"].String()] = &gdb.TableField{ - Index: i, - Name: m["FIELD"].String(), - Type: m["TYPE"].String(), - Null: isNull, - } - } - return fields - }, + strings.ToUpper(table), + ) ) - if v != nil { - fields = v.(map[string]*gdb.TableField) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil, err } - return + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + result, err = d.DoSelect(ctx, link, structureSql) + if err != nil { + return nil, err + } + fields = make(map[string]*gdb.TableField) + for i, m := range result { + isNull := false + if m["NULLABLE"].String() == "Y" { + isNull = true + } + + fields[m["FIELD"].String()] = &gdb.TableField{ + Index: i, + Name: m["FIELD"].String(), + Type: m["TYPE"].String(), + Null: isNull, + } + } + return fields, nil } // DoInsert inserts or updates data for given table. diff --git a/contrib/drivers/oracle/oracle_z_basic_test.go b/contrib/drivers/oracle/oracle_z_basic_test.go index bc3d61bb5..24444c866 100644 --- a/contrib/drivers/oracle/oracle_z_basic_test.go +++ b/contrib/drivers/oracle/oracle_z_basic_test.go @@ -8,11 +8,12 @@ package oracle_test import ( "fmt" + "strings" + "testing" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/test/gtest" - "strings" - "testing" ) func TestTables(t *testing.T) { @@ -102,13 +103,6 @@ func TestTableFields(t *testing.T) { }) } -func TestFilteredLink(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - s := dblink.FilteredLink() - gtest.AssertEQ(s, "oracle:xxx@127.0.0.1:1521/XE") - }) - -} func TestDoInsert(t *testing.T) { gtest.C(t, func(t *gtest.T) { createTable("t_user") diff --git a/contrib/drivers/pgsql/pgsql.go b/contrib/drivers/pgsql/pgsql.go index c438eaa78..97c88344f 100644 --- a/contrib/drivers/pgsql/pgsql.go +++ b/contrib/drivers/pgsql/pgsql.go @@ -3,12 +3,12 @@ // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. + +// Package pgsql implements gdb.Driver, which supports operations for database PostgreSQL. // // Note: // 1. It needs manually import: _ "github.com/lib/pq" // 2. It does not support Save/Replace features. - -// Package pgsql implements gdb.Driver, which supports operations for PostgreSql. package pgsql import ( @@ -17,7 +17,8 @@ import ( "fmt" "strings" - "github.com/gogf/gf/v2/container/gmap" + _ "github.com/lib/pq" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" @@ -25,7 +26,7 @@ import ( "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" - _ "github.com/lib/pq" + "github.com/gogf/gf/v2/util/gutil" ) // Driver is the driver for postgresql database. @@ -33,11 +34,6 @@ type Driver struct { *gdb.Core } -var ( - // tableFieldsMap caches the table information retrieved from database. - tableFieldsMap = gmap.New(true) -) - const ( internalPrimaryKeyInCtx gctx.StrKey = "primary_key" ) @@ -62,12 +58,16 @@ func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { } // Open creates and returns an underlying sql.DB object for pgsql. +// https://pkg.go.dev/github.com/lib/pq func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { var ( source string underlyingDriverName = "postgres" ) if config.Link != "" { + // ============================================================================ + // Deprecated from v2.2.0. + // ============================================================================ source = config.Link // Custom changing the schema in runtime. if config.Name != "" { @@ -89,6 +89,16 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { if config.Timezone != "" { source = fmt.Sprintf("%s timezone=%s", source, config.Timezone) } + + if config.Extra != "" { + var extraMap map[string]interface{} + if extraMap, err = gstr.Parse(config.Extra); err != nil { + return nil, err + } + for k, v := range extraMap { + source += fmt.Sprintf(` %s=%s`, k, v) + } + } } if db, err = sql.Open(underlyingDriverName, source); err != nil { @@ -101,21 +111,6 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { return } -// FilteredLink retrieves and returns filtered `linkInfo` that can be using for -// logging or tracing purpose. -func (d *Driver) FilteredLink() string { - linkInfo := d.GetConfig().Link - if linkInfo == "" { - return "" - } - s, _ := gregex.ReplaceString( - `(.+?)\s*password=(.+)\s*host=(.+)`, - `$1 password=xxx host=$3`, - linkInfo, - ) - return s -} - // GetChars returns the security char for this type of database. func (d *Driver) GetChars() (charLeft string, charRight string) { return `"`, `"` @@ -227,17 +222,10 @@ func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args [ // Tables retrieves and returns the tables of current schema. // It's mainly used in cli tool chain for automatically generating the models. func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, err error) { - var result gdb.Result - link, err := d.SlaveLink(schema...) - if err != nil { - return nil, err - } - querySchema := "public" - if len(schema) > 0 && schema[0] != "" { - querySchema = schema[0] - } - // list table names exclude partitions - query := fmt.Sprintf(` + var ( + result gdb.Result + querySchema = gutil.GetOrDefaultStr("public", schema...) + query = fmt.Sprintf(` SELECT c.relname FROM @@ -251,8 +239,13 @@ WHERE AND PG_TABLE_IS_VISIBLE(c.oid) ORDER BY c.relname`, - querySchema, + querySchema, + ) ) + link, err := d.SlaveLink(schema...) + if err != nil { + return nil, err + } query, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(query)) result, err = d.DoSelect(ctx, link, query) if err != nil { @@ -270,26 +263,11 @@ ORDER BY // // Also see DriverMysql.TableFields. func (d *Driver) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*gdb.TableField, err error) { - charL, charR := d.GetChars() - table = gstr.Trim(table, charL+charR) - if gstr.Contains(table, " ") { - return nil, gerror.NewCode( - gcode.CodeInvalidParameter, - "function TableFields supports only single table operations", - ) - } - table, _ = gregex.ReplaceString("\"", "", table) - useSchema := d.GetSchema() - if len(schema) > 0 && schema[0] != "" { - useSchema = schema[0] - } - v := tableFieldsMap.GetOrSetFuncLock( - fmt.Sprintf(`pgsql_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), - func() interface{} { - var ( - result gdb.Result - link gdb.Link - structureSql = fmt.Sprintf(` + var ( + result gdb.Result + link gdb.Link + useSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) + structureSql = fmt.Sprintf(` SELECT a.attname AS field, t.typname AS type,a.attnotnull as null, (case when d.contype is not null then 'pri' else '' end) as key ,ic.column_default as default_value,b.description as comment @@ -299,40 +277,34 @@ FROM pg_attribute a left join pg_class c on a.attrelid = c.oid left join pg_constraint d on d.conrelid = c.oid and a.attnum = d.conkey[1] left join pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid - left join pg_type t ON a.atttypid = t.oid + left join pg_type t ON a.atttypid = t.oid left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname WHERE c.relname = '%s' and a.attisdropped is false and a.attnum > 0 ORDER BY a.attnum`, - table, - ) - ) - if link, err = d.SlaveLink(useSchema); err != nil { - return nil - } - structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) - result, err = d.DoSelect(ctx, link, structureSql) - if err != nil { - return nil - } - fields = make(map[string]*gdb.TableField) - for i, m := range result { - fields[m["field"].String()] = &gdb.TableField{ - Index: i, - Name: m["field"].String(), - Type: m["type"].String(), - Null: !m["null"].Bool(), - Key: m["key"].String(), - Default: m["default_value"].Val(), - Comment: m["comment"].String(), - } - } - return fields - }, + table, + ) ) - if v != nil { - fields = v.(map[string]*gdb.TableField) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil, err } - return + structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql)) + result, err = d.DoSelect(ctx, link, structureSql) + if err != nil { + return nil, err + } + fields = make(map[string]*gdb.TableField) + for i, m := range result { + fields[m["field"].String()] = &gdb.TableField{ + Index: i, + Name: m["field"].String(), + Type: m["type"].String(), + Null: !m["null"].Bool(), + Key: m["key"].String(), + Default: m["default_value"].Val(), + Comment: m["comment"].String(), + } + } + return fields, nil } // DoInsert is not supported in pgsql. @@ -357,12 +329,13 @@ func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list ) case gdb.InsertOptionDefault: - tableFields, err := d.TableFields(ctx, table) + tableFields, err := d.GetCore().GetDB().TableFields(ctx, table) if err == nil { for _, field := range tableFields { if field.Key == "pri" { pkField := *field ctx = context.WithValue(ctx, internalPrimaryKeyInCtx, pkField) + break } } } @@ -396,7 +369,7 @@ func (d *Driver) DoExec(ctx context.Context, link gdb.Link, sql string, args ... isUseCoreDoExec = true } - // check if it is a insert operation. + // check if it is an insert operation. if !isUseCoreDoExec && pkField.Name != "" && strings.Contains(sql, "INSERT INTO") { primaryKey = pkField.Name sql += " RETURNING " + primaryKey diff --git a/contrib/drivers/pgsql/pgsql_result.go b/contrib/drivers/pgsql/pgsql_result.go index a290d45f9..287407840 100644 --- a/contrib/drivers/pgsql/pgsql_result.go +++ b/contrib/drivers/pgsql/pgsql_result.go @@ -1,3 +1,9 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + package pgsql import "database/sql" diff --git a/contrib/drivers/pgsql/pgsql_db_test.go b/contrib/drivers/pgsql/pgsql_z_db_test.go similarity index 100% rename from contrib/drivers/pgsql/pgsql_db_test.go rename to contrib/drivers/pgsql/pgsql_z_db_test.go diff --git a/contrib/drivers/pgsql/pgsql_init_test.go b/contrib/drivers/pgsql/pgsql_z_init_test.go similarity index 100% rename from contrib/drivers/pgsql/pgsql_init_test.go rename to contrib/drivers/pgsql/pgsql_z_init_test.go diff --git a/contrib/drivers/pgsql/pgsql_model_test.go b/contrib/drivers/pgsql/pgsql_z_model_test.go similarity index 100% rename from contrib/drivers/pgsql/pgsql_model_test.go rename to contrib/drivers/pgsql/pgsql_z_model_test.go diff --git a/contrib/drivers/pgsql/pgsql_raw_test.go b/contrib/drivers/pgsql/pgsql_z_raw_test.go similarity index 100% rename from contrib/drivers/pgsql/pgsql_raw_test.go rename to contrib/drivers/pgsql/pgsql_z_raw_test.go diff --git a/contrib/drivers/pgsql/pgsql_test.go b/contrib/drivers/pgsql/pgsql_z_test.go similarity index 99% rename from contrib/drivers/pgsql/pgsql_test.go rename to contrib/drivers/pgsql/pgsql_z_test.go index 40a5f329f..0d139582d 100644 --- a/contrib/drivers/pgsql/pgsql_test.go +++ b/contrib/drivers/pgsql/pgsql_z_test.go @@ -16,7 +16,6 @@ import ( ) func Test_LastInsertId(t *testing.T) { - // err not nil gtest.C(t, func(t *gtest.T) { _, err := db.Model("notexist").Insert(g.List{ diff --git a/contrib/drivers/sqlite/sqlite.go b/contrib/drivers/sqlite/sqlite.go index 46150ec7c..03f7d77fb 100644 --- a/contrib/drivers/sqlite/sqlite.go +++ b/contrib/drivers/sqlite/sqlite.go @@ -3,12 +3,12 @@ // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. + +// Package sqlite implements gdb.Driver, which supports operations for database SQLite. // // Note: // 1. It needs manually import: _ "github.com/glebarez/go-sqlite" // 2. It does not support Save/Replace features. - -// Package sqlite implements gdb.Driver, which supports operations for SQLite. package sqlite import ( @@ -18,9 +18,10 @@ import ( "strings" _ "github.com/glebarez/go-sqlite" + "github.com/gogf/gf/v2/util/gutil" - "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/encoding/gurl" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gfile" @@ -33,12 +34,6 @@ type Driver struct { *gdb.Core } -var ( - // tableFieldsMap caches the table information retrieved from database. - tableFieldsMap = gmap.New(true) - ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`) -) - func init() { if err := gdb.Register(`sqlite`, New()); err != nil { panic(err) @@ -59,12 +54,16 @@ func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) { } // Open creates and returns a underlying sql.DB object for sqlite. +// https://github.com/glebarez/go-sqlite func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { var ( source string underlyingDriverName = "sqlite" ) if config.Link != "" { + // ============================================================================ + // Deprecated from v2.2.0. + // ============================================================================ source = config.Link } else { source = config.Name @@ -73,6 +72,28 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { if absolutePath, _ := gfile.Search(source); absolutePath != "" { source = absolutePath } + + // Multiple PRAGMAs can be specified, e.g.: + // path/to/some.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL) + if config.Extra != "" { + var ( + options string + extraMap map[string]interface{} + ) + if extraMap, err = gstr.Parse(config.Extra); err != nil { + return nil, err + } + for k, v := range extraMap { + if options != "" { + options += "&" + } + options += fmt.Sprintf(`_pragma=%s(%s)`, k, gurl.Encode(gconv.String(v))) + } + if len(options) > 1 { + source += "?" + options + } + } + if db, err = sql.Open(underlyingDriverName, source); err != nil { err = gerror.WrapCodef( gcode.CodeDbOperationError, err, @@ -83,12 +104,6 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) { return } -// FilteredLink retrieves and returns filtered `linkInfo` that can be using for -// logging or tracing purpose. -func (d *Driver) FilteredLink() string { - return d.GetConfig().Link -} - // GetChars returns the security char for this type of database. func (d *Driver) GetChars() (charLeft string, charRight string) { return "`", "`" @@ -123,59 +138,47 @@ func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string, // TableFields retrieves and returns the fields' information of specified table of current schema. // // Also see DriverMysql.TableFields. -func (d *Driver) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*gdb.TableField, err error) { - charL, charR := d.GetChars() - table = gstr.Trim(table, charL+charR) - if gstr.Contains(table, " ") { - return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") - } - useSchema := d.GetSchema() - if len(schema) > 0 && schema[0] != "" { - useSchema = schema[0] - } - v := tableFieldsMap.GetOrSetFuncLock( - fmt.Sprintf(`sqlite_table_fields_%s_%s@group:%s`, table, useSchema, d.GetGroup()), - func() interface{} { - var ( - result gdb.Result - link gdb.Link - ) - if link, err = d.SlaveLink(useSchema); err != nil { - return nil - } - result, err = d.DoSelect(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) - if err != nil { - return nil - } - fields = make(map[string]*gdb.TableField) - for i, m := range result { - mKey := "" - if m["pk"].Bool() { - mKey = "pri" - } - fields[strings.ToLower(m["name"].String())] = &gdb.TableField{ - Index: i, - Name: strings.ToLower(m["name"].String()), - Type: strings.ToLower(m["type"].String()), - Key: mKey, - Default: m["dflt_value"].Val(), - Null: !m["notnull"].Bool(), - } - } - return fields - }, +func (d *Driver) TableFields( + ctx context.Context, table string, schema ...string, +) (fields map[string]*gdb.TableField, err error) { + var ( + result gdb.Result + link gdb.Link + useSchema = gutil.GetOrDefaultStr(d.GetSchema(), schema...) ) - if v != nil { - fields = v.(map[string]*gdb.TableField) + if link, err = d.SlaveLink(useSchema); err != nil { + return nil, err } - return + result, err = d.DoSelect(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table)) + if err != nil { + return nil, err + } + fields = make(map[string]*gdb.TableField) + for i, m := range result { + mKey := "" + if m["pk"].Bool() { + mKey = "pri" + } + fields[m["name"].String()] = &gdb.TableField{ + Index: i, + Name: m["name"].String(), + Type: m["type"].String(), + Key: mKey, + Default: m["dflt_value"].Val(), + Null: !m["notnull"].Bool(), + } + } + return fields, nil } // DoInsert is not supported in sqlite. -func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption) (result sql.Result, err error) { +func (d *Driver) DoInsert( + ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption, +) (result sql.Result, err error) { switch option.InsertOption { case gdb.InsertOptionSave: - return nil, ErrorSave + return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`) + case gdb.InsertOptionIgnore, gdb.InsertOptionReplace: var ( keys []string // Field names. @@ -242,6 +245,7 @@ func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list } } return batchResult, nil + default: return d.Core.DoInsert(ctx, link, table, list, option) } diff --git a/contrib/drivers/sqlite/sqlite_core_test.go b/contrib/drivers/sqlite/sqlite_core_test.go index c580230b7..ee934e65e 100644 --- a/contrib/drivers/sqlite/sqlite_core_test.go +++ b/contrib/drivers/sqlite/sqlite_core_test.go @@ -1520,11 +1520,11 @@ func Test_TableFields(t *testing.T) { defer dropTable(tableName) var expect = map[string][]interface{}{ // fields type null key default extra comment - "id": {"integer", false, "pri", nil, "", ""}, - "passport": {"varchar(45)", false, "", "passport", "", ""}, - "password": {"varchar(128)", false, "", "password", "", ""}, - "nickname": {"varchar(45)", true, "", nil, "", ""}, - "create_time": {"datetime", true, "", nil, "", ""}, + "id": {"INTEGER", false, "pri", nil, "", ""}, + "passport": {"VARCHAR(45)", false, "", "passport", "", ""}, + "password": {"VARCHAR(128)", false, "", "password", "", ""}, + "nickname": {"VARCHAR(45)", true, "", nil, "", ""}, + "create_time": {"DATETIME", true, "", nil, "", ""}, } res, err := db.TableFields(context.Background(), tableName) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 600783ee4..b60f1f2b6 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -10,8 +10,10 @@ package gdb import ( "context" "database/sql" + "fmt" "time" + "github.com/gogf/gf/v2/container/garray" "github.com/gogf/gf/v2/container/gmap" "github.com/gogf/gf/v2/container/gtype" "github.com/gogf/gf/v2/container/gvar" @@ -23,6 +25,7 @@ import ( "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/util/grand" + "github.com/gogf/gf/v2/util/gutil" ) // DB defines the interfaces for ORM operations. @@ -168,12 +171,11 @@ type DB interface { GetCtx() context.Context // See Core.GetCtx. GetCore() *Core // See Core.GetCore GetChars() (charLeft string, charRight string) // See Core.GetChars. - Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. - TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. + Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. The driver must implement this function. + TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. The driver must implement this function. ConvertDataForRecord(ctx context.Context, data interface{}) (map[string]interface{}, error) // See Core.ConvertDataForRecord ConvertValueForLocal(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) // See Core.ConvertValueForLocal CheckLocalTypeForField(ctx context.Context, fieldType string, fieldValue interface{}) (string, error) // See Core.CheckLocalTypeForField - FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server. } // Core is the base struct for database management. @@ -275,9 +277,15 @@ type ( List = []Map // List is type of map array. ) +type CatchSQLManager struct { + SQLArray *garray.StrArray + DoCommit bool +} + const ( defaultModelSafe = false defaultCharset = `utf8` + defaultProtocol = `tcp` queryTypeNormal = 0 queryTypeCount = 1 unionTypeNormal = 0 @@ -288,10 +296,14 @@ const ( ctxTimeoutTypeExec = iota ctxTimeoutTypeQuery ctxTimeoutTypePrepare - commandEnvKeyForDryRun = "gf.gdb.dryrun" - modelForDaoSuffix = `ForDao` - dbRoleSlave = `slave` - contextKeyForDB gctx.StrKey = `DBInContext` + cachePrefixTableFields = `TableFields:` + cachePrefixSelectCache = `SelectCache:` + commandEnvKeyForDryRun = "gf.gdb.dryrun" + modelForDaoSuffix = `ForDao` + dbRoleSlave = `slave` + ctxKeyForDB gctx.StrKey = `CtxKeyForDB` + ctxKeyCatchSQL gctx.StrKey = `CtxKeyCatchSQL` + ctxKeyInternalProducedSQL gctx.StrKey = `CtxKeyInternalProducedSQL` ) const ( @@ -357,6 +369,13 @@ var ( // allDryRun sets dry-run feature for all database connections. // It is commonly used for command options for convenience. allDryRun = false + + // tableFieldsMap caches the table information retrieved from database. + tableFieldsMap = gmap.NewStrAnyMap(true) + + // [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] + linkPatternWithType = `(\w+):([\w\-]+):(.+?)@(.+?)\((.+?):(\d+)\)/{0,1}([\w\-]*)\?{0,1}(.*)` + linkPatternWithoutType = `([\w\-]+):(.+?)@(.+?)\((.+?):(\d+)\)/{0,1}([\w\-]*)\?{0,1}(.*)` ) func init() { @@ -366,13 +385,13 @@ func init() { // Register registers custom database driver to gdb. func Register(name string, driver Driver) error { - driverMap[name] = driver + driverMap[name] = newDriverWrapper(driver) return nil } // New creates and returns an ORM object with given configuration node. func New(node ConfigNode) (db DB, err error) { - return doNewByNode(node, "") + return newDBByConfigNode(&node, "") } // NewByGroup creates and returns an ORM object with global configurations. @@ -395,7 +414,7 @@ func NewByGroup(group ...string) (db DB, err error) { if _, ok := configs.config[groupName]; ok { var node *ConfigNode if node, err = getConfigNodeByGroup(groupName, true); err == nil { - return doNewByNode(*node, groupName) + return newDBByConfigNode(node, groupName) } return nil, err } @@ -406,18 +425,18 @@ func NewByGroup(group ...string) (db DB, err error) { ) } -// doNewByNode creates and returns an ORM object with given configuration node and group name. -func doNewByNode(node ConfigNode, group string) (db DB, err error) { +// newDBByConfigNode creates and returns an ORM object with given configuration node and group name. +func newDBByConfigNode(node *ConfigNode, group string) (db DB, err error) { c := &Core{ group: group, debug: gtype.NewBool(), cache: gcache.New(), links: gmap.NewStrAnyMap(true), logger: glog.New(), - config: &node, + config: node, } if v, ok := driverMap[node.Type]; ok { - if c.db, err = v.New(c, &node); err != nil { + if c.db, err = v.New(c, node); err != nil { return nil, err } return c.db, nil @@ -547,15 +566,11 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error } else { node = c.config } - // Default value checks. if node.Charset == "" { node.Charset = defaultCharset } // Changes the schema. - nodeSchema := c.schema - if len(schema) > 0 && schema[0] != "" { - nodeSchema = schema[0] - } + nodeSchema := gutil.GetOrDefaultStr(c.schema, schema...) if nodeSchema != "" { // Value copy. n := *node @@ -563,7 +578,11 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error node = &n } // Cache the underlying connection pool object by node. - v := c.links.GetOrSetFuncLock(node.String(), func() interface{} { + instanceNameByNode := fmt.Sprintf( + `%s@%s(%s:%s)/%s`, + node.User, node.Protocol, node.Host, node.Port, node.Name, + ) + v := c.links.GetOrSetFuncLock(instanceNameByNode, func() interface{} { intlog.Printf(ctx, `open new connection, master:%#v, config:%#v, node:%#v`, master, c.config, node) defer func() { if err != nil { diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index ea468e5d5..23672f91a 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -45,7 +45,7 @@ func (c *Core) Ctx(ctx context.Context) DB { configNode = c.db.GetConfig() ) *newCore = *c - // It creates a new DB object, which is commonly a wrapper for object `Core`. + // It creates a new DB object(NOT NEW CONNECTION), which is commonly a wrapper for object `Core`. newCore.db, err = driverMap[configNode.Type].New(newCore, configNode) if err != nil { // It is really a serious error here. @@ -177,9 +177,9 @@ func (c *Core) GetArray(ctx context.Context, sql string, args ...interface{}) ([ return all.Array(), nil } -// GetStruct queries one record from database and converts it to given struct. +// doGetStruct queries one record from database and converts it to given struct. // The parameter `pointer` should be a pointer to struct. -func (c *Core) GetStruct(ctx context.Context, pointer interface{}, sql string, args ...interface{}) error { +func (c *Core) doGetStruct(ctx context.Context, pointer interface{}, sql string, args ...interface{}) error { one, err := c.db.GetOne(ctx, sql, args...) if err != nil { return err @@ -187,9 +187,9 @@ func (c *Core) GetStruct(ctx context.Context, pointer interface{}, sql string, a return one.Struct(pointer) } -// GetStructs queries records from database and converts them to given struct. +// doGetStructs queries records from database and converts them to given struct. // The parameter `pointer` should be type of struct slice: []struct/[]*struct. -func (c *Core) GetStructs(ctx context.Context, pointer interface{}, sql string, args ...interface{}) error { +func (c *Core) doGetStructs(ctx context.Context, pointer interface{}, sql string, args ...interface{}) error { all, err := c.db.GetAll(ctx, sql, args...) if err != nil { return err @@ -214,10 +214,10 @@ func (c *Core) GetScan(ctx context.Context, pointer interface{}, sql string, arg } switch reflectInfo.OriginKind { case reflect.Array, reflect.Slice: - return c.db.GetCore().GetStructs(ctx, pointer, sql, args...) + return c.db.GetCore().doGetStructs(ctx, pointer, sql, args...) case reflect.Struct: - return c.db.GetCore().GetStruct(ctx, pointer, sql, args...) + return c.db.GetCore().doGetStruct(ctx, pointer, sql, args...) } return gerror.NewCodef( gcode.CodeInvalidParameter, @@ -641,7 +641,10 @@ func (c *Core) DoDelete(ctx context.Context, link Link, table string, condition // FilteredLink retrieves and returns filtered `linkInfo` that can be using for // logging or tracing purpose. func (c *Core) FilteredLink() string { - return c.config.Link + return fmt.Sprintf( + `%s@%s(%s:%s)/%s`, + c.config.User, c.config.Protocol, c.config.Host, c.config.Port, c.config.Name, + ) } // MarshalJSON implements the interface MarshalJSON for json.Marshal. diff --git a/database/gdb/gdb_core_config.go b/database/gdb/gdb_core_config.go index 233a41c81..1c5bfc59b 100644 --- a/database/gdb/gdb_core_config.go +++ b/database/gdb/gdb_core_config.go @@ -7,7 +7,6 @@ package gdb import ( - "fmt" "sync" "time" @@ -31,13 +30,15 @@ type ConfigNode struct { Pass string `json:"pass"` // Authentication password. Name string `json:"name"` // Default used database name. Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle. - Link string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored. + Link string `json:"link"` // (Optional) Custom link information for all configuration in one single string. + Extra string `json:"extra"` // (Optional) Extra configuration according the registered third-party database driver. Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave. Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output. Prefix string `json:"prefix"` // (Optional) Table prefix. DryRun bool `json:"dryRun"` // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements. Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node. Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database. + Protocol string `json:"protocol"` // (Optional, "tcp" in default) See net.Dial for more information which networks are available. Timezone string `json:"timezone"` // (Optional) Sets the time zone for displaying and interpreting time stamps. MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool. MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool. @@ -56,7 +57,7 @@ const ( DefaultGroupName = "default" // Default group name. ) -// configs is internal used configuration object. +// configs specifies internal used configuration object. var configs struct { sync.RWMutex config Config // All configurations. @@ -200,19 +201,6 @@ func (c *Core) SetMaxConnLifeTime(d time.Duration) { c.config.MaxConnLifeTime = d } -// String returns the node as string. -func (node *ConfigNode) String() string { - return fmt.Sprintf( - `%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d#%s`, - node.User, node.Host, node.Port, - node.Name, node.Type, node.Role, node.Charset, node.Debug, - node.MaxIdleConnCount, - node.MaxOpenConnCount, - node.MaxConnLifeTime, - node.Link, - ) -} - // GetConfig returns the current used node configuration. func (c *Core) GetConfig() *ConfigNode { return c.config diff --git a/database/gdb/gdb_core_trace.go b/database/gdb/gdb_core_trace.go index 020fe928a..2d6e7b75d 100644 --- a/database/gdb/gdb_core_trace.go +++ b/database/gdb/gdb_core_trace.go @@ -62,8 +62,8 @@ func (c *Core) traceSpanEnd(ctx context.Context, span trace.Span, sql *Sql) { if c.db.GetConfig().User != "" { labels = append(labels, attribute.String(traceAttrDbUser, c.db.GetConfig().User)) } - if filteredLink := c.db.FilteredLink(); filteredLink != "" { - labels = append(labels, attribute.String(traceAttrDbLink, c.db.FilteredLink())) + if filteredLink := c.db.GetCore().FilteredLink(); filteredLink != "" { + labels = append(labels, attribute.String(traceAttrDbLink, c.db.GetCore().FilteredLink())) } if group := c.db.GetGroup(); group != "" { labels = append(labels, attribute.String(traceAttrDbGroup, group)) diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index 37737fa1e..070d4b19f 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -58,6 +58,17 @@ func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...inter if err != nil { return nil, err } + // SQL format and retrieve. + if v := ctx.Value(ctxKeyCatchSQL); v != nil { + var ( + manager = v.(*CatchSQLManager) + formattedSql = FormatSqlWithArgs(sql, args) + ) + manager.SQLArray.Append(formattedSql) + if !manager.DoCommit && ctx.Value(ctxKeyInternalProducedSQL) == nil { + return nil, nil + } + } // Link execution. var out DoCommitOutput out, err = c.db.DoCommit(ctx, DoCommitInput{ @@ -102,12 +113,23 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf defer cancelFunc() } - // Sql filtering. + // SQL filtering. sql, args = formatSql(sql, args) sql, args, err = c.db.DoFilter(ctx, link, sql, args) if err != nil { return nil, err } + // SQL format and retrieve. + if v := ctx.Value(ctxKeyCatchSQL); v != nil { + var ( + manager = v.(*CatchSQLManager) + formattedSql = FormatSqlWithArgs(sql, args) + ) + manager.SQLArray.Append(formattedSql) + if !manager.DoCommit && ctx.Value(ctxKeyInternalProducedSQL) == nil { + return new(SqlResult), nil + } + } // Link execution. var out DoCommitOutput out, err = c.db.DoCommit(ctx, DoCommitInput{ diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index d995e0df5..f8ebad934 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -9,36 +9,20 @@ package gdb import ( "context" + "fmt" + "github.com/gogf/gf/v2/crypto/gmd5" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" ) -// WithDB injects given db object into context and returns a new context. -func WithDB(ctx context.Context, db DB) context.Context { - if db == nil { - return ctx - } - dbCtx := db.GetCtx() - if ctxDb := DBFromCtx(dbCtx); ctxDb != nil { - return dbCtx - } - ctx = context.WithValue(ctx, contextKeyForDB, db) - return ctx -} - -// DBFromCtx retrieves and returns DB object from context. -func DBFromCtx(ctx context.Context) DB { - if ctx == nil { - return nil - } - v := ctx.Value(contextKeyForDB) - if v != nil { - return v.(DB) - } - return nil +// GetDB returns the underlying DB. +func (c *Core) GetDB() DB { + return c.db } // GetLink creates and returns the underlying database link object with transaction checks. @@ -155,6 +139,59 @@ func (c *Core) TableFields(ctx context.Context, table string, schema ...string) return } +// ClearTableFields removes certain cached table fields of current configuration group. +func (c *Core) ClearTableFields(ctx context.Context, table string, schema ...string) (err error) { + tableFieldsMap.Remove(fmt.Sprintf( + `%s%s@%s#%s`, + cachePrefixTableFields, + c.db.GetGroup(), + gutil.GetOrDefaultStr(c.db.GetSchema(), schema...), + table, + )) + return +} + +// ClearTableFieldsAll removes all cached table fields of current configuration group. +func (c *Core) ClearTableFieldsAll(ctx context.Context) (err error) { + var ( + keys = tableFieldsMap.Keys() + cachePrefix = fmt.Sprintf(`%s@%s`, cachePrefixTableFields, c.db.GetGroup()) + removedKeys = make([]string, 0) + ) + for _, key := range keys { + if gstr.HasPrefix(key, cachePrefix) { + removedKeys = append(removedKeys, key) + } + } + if len(removedKeys) > 0 { + tableFieldsMap.Removes(removedKeys) + } + return +} + +// ClearCache removes cached sql result of certain table. +func (c *Core) ClearCache(ctx context.Context, table string) (err error) { + return c.db.GetCache().Clear(ctx) +} + +// ClearCacheAll removes all cached sql result from cache +func (c *Core) ClearCacheAll(ctx context.Context) (err error) { + return c.db.GetCache().Clear(ctx) +} + +func (c *Core) makeSelectCacheKey(name, schema, table, sql string, args ...interface{}) string { + if name == "" { + name = fmt.Sprintf( + `%s@%s#%s:%s`, + c.db.GetGroup(), + schema, + table, + gmd5.MustEncryptString(sql+", @PARAMS:"+gconv.String(args)), + ) + } + return fmt.Sprintf(`%s%s`, cachePrefixSelectCache, name) +} + // HasField determine whether the field exists in the table. func (c *Core) HasField(ctx context.Context, table, field string, schema ...string) (bool, error) { table = c.guessPrimaryTableName(table) diff --git a/database/gdb/gdb_default_driver.go b/database/gdb/gdb_driver_default.go similarity index 67% rename from database/gdb/gdb_default_driver.go rename to database/gdb/gdb_driver_default.go index 82168e597..0df39ee10 100644 --- a/database/gdb/gdb_default_driver.go +++ b/database/gdb/gdb_driver_default.go @@ -10,37 +10,37 @@ import ( "database/sql" ) -// DriverTest is the driver for mysql database. -type DriverTest struct { +// DriverDefault is the default driver for mysql database, which does nothing. +type DriverDefault struct { *Core } func init() { - if err := Register("test", &DriverTest{}); err != nil { + if err := Register("default", &DriverDefault{}); err != nil { panic(err) } } // New creates and returns a database object for mysql. // It implements the interface of gdb.Driver for extra database driver installation. -func (d *DriverTest) New(core *Core, node *ConfigNode) (DB, error) { - return &DriverTest{ +func (d *DriverDefault) New(core *Core, node *ConfigNode) (DB, error) { + return &DriverDefault{ Core: core, }, nil } // Open creates and returns an underlying sql.DB object for mysql. // Note that it converts time.Time argument to local timezone in default. -func (d *DriverTest) Open(config *ConfigNode) (db *sql.DB, err error) { +func (d *DriverDefault) Open(config *ConfigNode) (db *sql.DB, err error) { return } // PingMaster pings the master node to check authentication or keeps the connection alive. -func (d *DriverTest) PingMaster() error { +func (d *DriverDefault) PingMaster() error { return nil } // PingSlave pings the slave node to check authentication or keeps the connection alive. -func (d *DriverTest) PingSlave() error { +func (d *DriverDefault) PingSlave() error { return nil } diff --git a/database/gdb/gdb_driver_wrapper.go b/database/gdb/gdb_driver_wrapper.go new file mode 100644 index 000000000..7fb5560a1 --- /dev/null +++ b/database/gdb/gdb_driver_wrapper.go @@ -0,0 +1,31 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +// DriverWrapper is a driver wrapper for extending features with embedded driver. +type DriverWrapper struct { + driver Driver +} + +// New creates and returns a database object for mysql. +// It implements the interface of gdb.Driver for extra database driver installation. +func (d *DriverWrapper) New(core *Core, node *ConfigNode) (DB, error) { + db, err := d.driver.New(core, node) + if err != nil { + return nil, err + } + return &DriverWrapperDB{ + DB: db, + }, nil +} + +// newDriverWrapper creates and returns a driver wrapper. +func newDriverWrapper(driver Driver) Driver { + return &DriverWrapper{ + driver: driver, + } +} diff --git a/database/gdb/gdb_driver_wrapper_db.go b/database/gdb/gdb_driver_wrapper_db.go new file mode 100644 index 000000000..7a58ec88b --- /dev/null +++ b/database/gdb/gdb_driver_wrapper_db.go @@ -0,0 +1,132 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gdb + +import ( + "context" + "database/sql" + "fmt" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/text/gregex" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// DriverWrapperDB is a DB wrapper for extending features with embedded DB. +type DriverWrapperDB struct { + DB +} + +// Open creates and returns an underlying sql.DB object for pgsql. +// https://pkg.go.dev/github.com/lib/pq +func (d *DriverWrapperDB) Open(config *ConfigNode) (db *sql.DB, err error) { + if config.Link != "" { + config = parseConfigNodeLink(config) + } + return d.DB.Open(config) +} + +// Tables retrieves and returns the tables of current schema. +// It's mainly used in cli tool chain for automatically generating the models. +func (d *DriverWrapperDB) Tables(ctx context.Context, schema ...string) (tables []string, err error) { + ctx = context.WithValue(ctx, ctxKeyInternalProducedSQL, struct{}{}) + return d.DB.Tables(ctx, schema...) +} + +// TableFields retrieves and returns the fields' information of specified table of current +// schema. +// +// The parameter `link` is optional, if given nil it automatically retrieves a raw sql connection +// as its link to proceed necessary sql query. +// +// Note that it returns a map containing the field name and its corresponding fields. +// As a map is unsorted, the TableField struct has an "Index" field marks its sequence in +// the fields. +// +// It's using cache feature to enhance the performance, which is never expired util the +// process restarts. +func (d *DriverWrapperDB) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { + charL, charR := d.GetChars() + table = gstr.Trim(table, charL+charR) + if gstr.Contains(table, " ") { + return nil, gerror.NewCode( + gcode.CodeInvalidParameter, + "function TableFields supports only single table operations", + ) + } + var ( + cacheKey = fmt.Sprintf( + `%s%s@%s#%s`, + cachePrefixTableFields, + d.GetGroup(), + gutil.GetOrDefaultStr(d.GetSchema(), schema...), + table, + ) + value = tableFieldsMap.GetOrSetFuncLock(cacheKey, func() interface{} { + ctx = context.WithValue(ctx, ctxKeyInternalProducedSQL, struct{}{}) + fields, err = d.DB.TableFields(ctx, table, schema...) + if err != nil { + return nil + } + return fields + }) + ) + if value != nil { + fields = value.(map[string]*TableField) + } + return +} + +func parseConfigNodeLink(node *ConfigNode) *ConfigNode { + var match []string + // It firstly parses `link` using with type pattern. + match, _ = gregex.MatchString(linkPatternWithType, node.Link) + if len(match) > 6 { + node.Type = match[1] + node.User = match[2] + node.Pass = match[3] + node.Protocol = match[4] + node.Host = match[5] + node.Port = match[6] + node.Name = match[7] + if len(match) > 7 { + node.Extra = match[8] + } + node.Link = "" + } else { + // Else it parses `link` using without type pattern. + match, _ = gregex.MatchString(linkPatternWithoutType, node.Link) + if len(match) > 6 { + node.User = match[1] + node.Pass = match[2] + node.Protocol = match[3] + node.Host = match[4] + node.Port = match[5] + node.Name = match[6] + if len(match) > 7 { + node.Extra = match[7] + } + node.Link = "" + } + } + if node.Extra != "" { + if m, _ := gstr.Parse(node.Extra); len(m) > 0 { + _ = gconv.Struct(m, &node) + } + } + // Default value checks. + if node.Charset == "" { + node.Charset = defaultCharset + } + if node.Protocol == "" { + node.Protocol = defaultProtocol + } + return node +} diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 454d8fa82..421700b56 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/gogf/gf/v2/container/garray" "github.com/gogf/gf/v2/internal/empty" "github.com/gogf/gf/v2/internal/reflection" "github.com/gogf/gf/v2/internal/utils" @@ -64,6 +65,54 @@ var ( structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...) ) +// WithDB injects given db object into context and returns a new context. +func WithDB(ctx context.Context, db DB) context.Context { + if db == nil { + return ctx + } + dbCtx := db.GetCtx() + if ctxDb := DBFromCtx(dbCtx); ctxDb != nil { + return dbCtx + } + ctx = context.WithValue(ctx, ctxKeyForDB, db) + return ctx +} + +// DBFromCtx retrieves and returns DB object from context. +func DBFromCtx(ctx context.Context) DB { + if ctx == nil { + return nil + } + v := ctx.Value(ctxKeyForDB) + if v != nil { + return v.(DB) + } + return nil +} + +// ToSQL formats and returns the last one of sql statements in given closure function. +func ToSQL(ctx context.Context, f func(ctx context.Context) error) (sql string, err error) { + var manager = &CatchSQLManager{ + SQLArray: garray.NewStrArray(), + DoCommit: false, + } + ctx = context.WithValue(ctx, ctxKeyCatchSQL, manager) + err = f(ctx) + sql, _ = manager.SQLArray.PopRight() + return +} + +// CatchSQL catches and returns all sql statements that are executed in given closure function. +func CatchSQL(ctx context.Context, f func(ctx context.Context) error) (sqlArray []string, err error) { + var manager = &CatchSQLManager{ + SQLArray: garray.NewStrArray(), + DoCommit: true, + } + ctx = context.WithValue(ctx, ctxKeyCatchSQL, manager) + err = f(ctx) + return manager.SQLArray.Slice(), err +} + // isDoStruct checks and returns whether given type is a DO struct. func isDoStruct(object interface{}) bool { // It checks by struct name like "XxxForDao", to be compatible with old version. diff --git a/database/gdb/gdb_model_cache.go b/database/gdb/gdb_model_cache.go index ec28855d2..e4195da2e 100644 --- a/database/gdb/gdb_model_cache.go +++ b/database/gdb/gdb_model_cache.go @@ -8,13 +8,10 @@ package gdb import ( "context" - "fmt" "time" - "github.com/gogf/gf/v2/crypto/gmd5" "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/internal/json" - "github.com/gogf/gf/v2/util/gconv" ) type CacheOption struct { @@ -57,7 +54,8 @@ func (m *Model) Cache(option CacheOption) *Model { // cache feature is enabled. func (m *Model) checkAndRemoveSelectCache(ctx context.Context) { if m.cacheEnabled && m.cacheOption.Duration < 0 && len(m.cacheOption.Name) > 0 { - if _, err := m.db.GetCache().Remove(ctx, m.cacheOption.Name); err != nil { + var cacheKey = m.makeSelectCacheKey("") + if _, err := m.db.GetCache().Remove(ctx, cacheKey); err != nil { intlog.Errorf(ctx, `%+v`, err) } } @@ -128,13 +126,11 @@ func (m *Model) saveSelectResultToCache(ctx context.Context, result Result, sql } func (m *Model) makeSelectCacheKey(sql string, args ...interface{}) string { - var cacheKey = m.cacheOption.Name - if len(cacheKey) == 0 { - cacheKey = fmt.Sprintf( - `GCache@Schema(%s):%s`, - m.db.GetSchema(), - gmd5.MustEncryptString(sql+", @PARAMS:"+gconv.String(args)), - ) - } - return cacheKey + return m.db.GetCore().makeSelectCacheKey( + m.cacheOption.Name, + m.db.GetSchema(), + m.db.GetCore().guessPrimaryTableName(m.tables), + sql, + args..., + ) } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 08ecab7c0..ee1ab2e61 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -32,13 +32,13 @@ func (m *Model) QuoteWord(s string) string { // Also see DriverMysql.TableFields. func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) { var ( - table = m.db.GetCore().guessPrimaryTableName(tableStr) - useSchema = m.schema + table = m.db.GetCore().guessPrimaryTableName(tableStr) + usedSchema = m.schema ) if len(schema) > 0 && schema[0] != "" { - useSchema = schema[0] + usedSchema = schema[0] } - return m.db.TableFields(m.GetCtx(), table, useSchema) + return m.db.TableFields(m.GetCtx(), table, usedSchema) } // getModel creates and returns a cloned model of current model if `safe` is true, or else it returns diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 840dd4224..0cef9bd4c 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -18,6 +18,251 @@ var ( ctx = context.TODO() ) +func Test_parseConfigNodeLink_WithType(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/khaos_oss?loc=Local&parseTime=true&charset=latin`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, `khaos_oss`) + t.Assert(newNode.Extra, `loc=Local&parseTime=true&charset=latin`) + t.Assert(newNode.Charset, `latin`) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/khaos_oss?`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, `khaos_oss`) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/khaos_oss`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, `khaos_oss`) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + // empty database preselect. + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/?loc=Local&parseTime=true&charset=latin`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, `loc=Local&parseTime=true&charset=latin`) + t.Assert(newNode.Charset, `latin`) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@tcp(9.135.69.119:3306)?loc=Local&parseTime=true&charset=latin`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, `loc=Local&parseTime=true&charset=latin`) + t.Assert(newNode.Charset, `latin`) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@tcp(9.135.69.119:3306)`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + // udp. + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `mysql:root:CxzhD*624:27jh@udp(9.135.69.119:3306)`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, `mysql`) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `udp`) + }) +} + +func Test_parseConfigNodeLink_WithoutType(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/khaos_oss?loc=Local&parseTime=true&charset=latin`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, ``) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, `khaos_oss`) + t.Assert(newNode.Extra, `loc=Local&parseTime=true&charset=latin`) + t.Assert(newNode.Charset, `latin`) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/khaos_oss?`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, ``) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, `khaos_oss`) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/khaos_oss`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.Type, ``) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, `khaos_oss`) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + // empty database preselect. + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/?loc=Local&parseTime=true&charset=latin`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, `loc=Local&parseTime=true&charset=latin`) + t.Assert(newNode.Charset, `latin`) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@tcp(9.135.69.119:3306)?loc=Local&parseTime=true&charset=latin`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, `loc=Local&parseTime=true&charset=latin`) + t.Assert(newNode.Charset, `latin`) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@tcp(9.135.69.119:3306)/`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@tcp(9.135.69.119:3306)`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `tcp`) + }) + // protocol. + gtest.C(t, func(t *gtest.T) { + node := &ConfigNode{ + Link: `root:CxzhD*624:27jh@udp(9.135.69.119:3306)`, + } + newNode := parseConfigNodeLink(node) + t.Assert(newNode.User, `root`) + t.Assert(newNode.Pass, `CxzhD*624:27jh`) + t.Assert(newNode.Host, `9.135.69.119`) + t.Assert(newNode.Port, `3306`) + t.Assert(newNode.Name, ``) + t.Assert(newNode.Extra, ``) + t.Assert(newNode.Charset, defaultCharset) + t.Assert(newNode.Protocol, `udp`) + }) +} + func Test_Func_doQuoteWord(t *testing.T) { gtest.C(t, func(t *gtest.T) { array := map[string]string{ diff --git a/encoding/gjson/gjson.go b/encoding/gjson/gjson.go index f103be4a6..23e685fa3 100644 --- a/encoding/gjson/gjson.go +++ b/encoding/gjson/gjson.go @@ -21,15 +21,17 @@ import ( "github.com/gogf/gf/v2/util/gconv" ) +type ContentType string + const ( - ContentTypeJson = `json` - ContentTypeJs = `js` - ContentTypeXml = `xml` - ContentTypeIni = `ini` - ContentTypeYaml = `yaml` - ContentTypeYml = `yml` - ContentTypeToml = `toml` - ContentTypeProperties = `properties` + ContentTypeJson ContentType = `json` + ContentTypeJs ContentType = `js` + ContentTypeXml ContentType = `xml` + ContentTypeIni ContentType = `ini` + ContentTypeYaml ContentType = `yaml` + ContentTypeYml ContentType = `yml` + ContentTypeToml ContentType = `toml` + ContentTypeProperties ContentType = `properties` ) const ( @@ -46,10 +48,10 @@ type Json struct { // Options for Json object creating/loading. type Options struct { - Safe bool // Mark this object is for in concurrent-safe usage. This is especially for Json object creating. - Tags string // Custom priority tags for decoding, eg: "json,yaml,MyTag". This is especially for struct parsing into Json object. - Type string // Type specifies the data content type, eg: json, xml, yaml, toml, ini. - StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64. + Safe bool // Mark this object is for in concurrent-safe usage. This is especially for Json object creating. + Tags string // Custom priority tags for decoding, eg: "json,yaml,MyTag". This is especially for struct parsing into Json object. + Type ContentType // Type specifies the data content type, eg: json, xml, yaml, toml, ini. + StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64. } // iInterfaces is used for type assert api for Interfaces(). diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index adcaa9b1a..5d6dfd910 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -32,7 +32,7 @@ import ( // The parameter `safe` specifies whether using this Json object in concurrent-safe context, // which is false in default. func New(data interface{}, safe ...bool) *Json { - return NewWithTag(data, ContentTypeJson, safe...) + return NewWithTag(data, string(ContentTypeJson), safe...) } // NewWithTag creates a Json object with any variable type of `data`, but `data` should be a map @@ -107,7 +107,7 @@ func Load(path string, safe ...bool) (*Json, error) { path = p } options := Options{ - Type: gfile.Ext(path), + Type: ContentType(gfile.Ext(path)), } if len(safe) > 0 && safe[0] { options.Safe = true @@ -200,7 +200,7 @@ func LoadContent(data interface{}, safe ...bool) (*Json, error) { // LoadContentType creates a Json object from given type and content, // supporting data content type as follows: // JSON, XML, INI, YAML and TOML. -func LoadContentType(dataType string, data interface{}, safe ...bool) (*Json, error) { +func LoadContentType(dataType ContentType, data interface{}, safe ...bool) (*Json, error) { content := gconv.Bytes(data) if len(content) == 0 { return New(nil, safe...), nil @@ -220,7 +220,7 @@ func LoadContentType(dataType string, data interface{}, safe ...bool) (*Json, er } // IsValidDataType checks and returns whether given `dataType` a valid data type for loading. -func IsValidDataType(dataType string) bool { +func IsValidDataType(dataType ContentType) bool { if dataType == "" { return false } @@ -279,7 +279,9 @@ func doLoadContentWithOptions(data []byte, options Options) (*Json, error) { if options.Type == "" { options.Type = checkDataType(data) } - options.Type = gstr.TrimLeft(options.Type, ".") + options.Type = ContentType(gstr.TrimLeft( + string(options.Type), "."), + ) switch options.Type { case ContentTypeJson, ContentTypeJs: @@ -334,7 +336,7 @@ func doLoadContentWithOptions(data []byte, options Options) (*Json, error) { // checkDataType automatically checks and returns the data type for `content`. // Note that it uses regular expression for loose checking, you can use LoadXXX/LoadContentType // functions to load the content for certain content type. -func checkDataType(content []byte) string { +func checkDataType(content []byte) ContentType { if json.Valid(content) { return ContentTypeJson } else if gregex.IsMatch(`^<.+>[\S\s]+<.+>\s*$`, content) { diff --git a/example/config/kubecm/boot_in_pod/boot.go b/example/config/kubecm/boot_in_pod/boot.go new file mode 100644 index 000000000..094aaa24d --- /dev/null +++ b/example/config/kubecm/boot_in_pod/boot.go @@ -0,0 +1,30 @@ +package boot + +import ( + "github.com/gogf/gf/contrib/config/kubecm/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" +) + +const ( + configmapName = "test-configmap" + dataItemInConfigmap = "config.yaml" +) + +func init() { + var ( + err error + ctx = gctx.GetInitCtx() + ) + // Create kubecm Client that implements gcfg.Adapter. + adapter, err := kubecm.New(gctx.GetInitCtx(), kubecm.Config{ + ConfigMap: configmapName, + DataItem: dataItemInConfigmap, + }) + if err != nil { + g.Log().Fatalf(ctx, `%+v`, err) + } + + // Change the adapter of default configuration instance. + g.Cfg().SetAdapter(adapter) +} diff --git a/example/config/kubecm/boot_out_pod/boot.go b/example/config/kubecm/boot_out_pod/boot.go new file mode 100644 index 000000000..a51230f09 --- /dev/null +++ b/example/config/kubecm/boot_out_pod/boot.go @@ -0,0 +1,44 @@ +package boot + +import ( + "k8s.io/client-go/kubernetes" + + "github.com/gogf/gf/contrib/config/kubecm/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" +) + +const ( + namespace = "default" + configmapName = "test-configmap" + dataItemInConfigmap = "config.yaml" + kubeConfigFilePathJohn = `/Users/john/.kube/config` +) + +func init() { + var ( + err error + ctx = gctx.GetInitCtx() + kubeClient *kubernetes.Clientset + ) + // Create kubernetes client. + // It is optional creating kube client when its is running in pod. + kubeClient, err = kubecm.NewKubeClientFromPath(ctx, kubeConfigFilePathJohn) + if err != nil { + g.Log().Fatalf(ctx, `%+v`, err) + } + // Create kubecm Client that implements gcfg.Adapter. + adapter, err := kubecm.New(gctx.GetInitCtx(), kubecm.Config{ + ConfigMap: configmapName, + DataItem: dataItemInConfigmap, + Namespace: namespace, // It is optional specifying namespace when its is running in pod. + KubeClient: kubeClient, // It is optional specifying kube client when its is running in pod. + }) + if err != nil { + g.Log().Fatalf(ctx, `%+v`, err) + } + + // Change the adapter of default configuration instance. + g.Cfg().SetAdapter(adapter) + +} diff --git a/example/config/kubecm/main.go b/example/config/kubecm/main.go new file mode 100644 index 000000000..31bf83ddc --- /dev/null +++ b/example/config/kubecm/main.go @@ -0,0 +1,21 @@ +package main + +import ( + _ "github.com/gogf/gf/example/config/kubecm/boot_in_pod" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" +) + +func main() { + var ctx = gctx.GetInitCtx() + + // Available checks. + g.Dump(g.Cfg().Available(ctx)) + + // All key-value configurations. + g.Dump(g.Cfg().Data(ctx)) + + // Retrieve certain value by key. + g.Dump(g.Cfg().MustGet(ctx, "server.address")) +} diff --git a/example/go.mod b/example/go.mod index 5c05a6661..babbfa20d 100644 --- a/example/go.mod +++ b/example/go.mod @@ -3,19 +3,22 @@ module github.com/gogf/gf/example go 1.15 require ( - github.com/gogf/gf/contrib/drivers/mysql/v2 v2.1.0-rc3 + github.com/gogf/gf/contrib/config/kubecm/v2 v2.0.0 + github.com/gogf/gf/contrib/drivers/mysql/v2 v2.0.0 github.com/gogf/gf/contrib/registry/etcd/v2 v2.1.0-rc3.0.20220523034830-510fa3faf03f - github.com/gogf/gf/contrib/registry/polaris/v2 v2.0.0-rc2 - github.com/gogf/gf/contrib/trace/jaeger/v2 v2.0.0-rc2 + github.com/gogf/gf/contrib/registry/polaris/v2 v2.0.0 + github.com/gogf/gf/contrib/trace/jaeger/v2 v2.0.0 github.com/gogf/gf/v2 v2.1.0-rc4.0.20220620123459-52056644d499 github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.2 github.com/polarismesh/polaris-go v1.2.0-beta.0.0.20220517041223-596a6a63b00f google.golang.org/grpc v1.46.2 + k8s.io/client-go v0.25.2 ) replace ( + github.com/gogf/gf/contrib/config/kubecm/v2 => ../contrib/config/kubecm github.com/gogf/gf/contrib/drivers/mysql/v2 => ../contrib/drivers/mysql/ github.com/gogf/gf/contrib/registry/etcd/v2 => ../contrib/registry/etcd/ github.com/gogf/gf/contrib/registry/polaris/v2 => ../contrib/registry/polaris/ diff --git a/example/go.sum b/example/go.sum index bd502d77d..afd2f4d4a 100644 --- a/example/go.sum +++ b/example/go.sum @@ -13,6 +13,18 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -31,10 +43,30 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -43,6 +75,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -50,6 +84,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -60,8 +96,10 @@ github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9 github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/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-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -69,25 +107,41 @@ github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmf github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= +github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 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= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -99,11 +153,27 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= @@ -114,12 +184,16 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773 h1:YQBLawktoymYtPGs9idE9JS5Wqd3SjIzUEZOPKCdSw0= github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773/go.mod h1:Z0GCeHXz1UI0HtA0K45c6TzEGM4DL/PLatS747/WarI= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -127,6 +201,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -142,8 +218,10 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y= @@ -153,6 +231,9 @@ github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 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= @@ -160,14 +241,21 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/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.1/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.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/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.6/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-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -175,17 +263,30 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -199,10 +300,16 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -210,18 +317,25 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -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.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= @@ -233,33 +347,52 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 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/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 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/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -300,15 +433,23 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 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/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -318,6 +459,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= @@ -329,6 +472,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= go.opentelemetry.io/otel/exporters/jaeger v1.7.0 h1:wXgjiRldljksZkZrldGVe6XrG9u3kYDyQmkZwmm5dI0= @@ -351,8 +496,13 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -375,6 +525,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= @@ -385,8 +536,12 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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= @@ -402,6 +557,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -418,20 +574,41 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 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= @@ -443,6 +620,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -454,6 +632,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -479,32 +658,53 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/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-20210423185535-09eb48e85fd7/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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -513,7 +713,11 @@ golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/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-20181030221726-6c7e314b6563/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= @@ -524,6 +728,7 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -548,6 +753,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -555,16 +761,26 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -582,12 +798,26 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -618,7 +848,32 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220504150022-98cd25cafc72 h1:iif0mpUetMBqcQPUoq+JnCcmzvfpp8wRx515va8wP1c= google.golang.org/genproto v0.0.0-20220504150022-98cd25cafc72/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -633,12 +888,23 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -657,10 +923,13 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -675,8 +944,10 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -684,7 +955,37 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg= +k8s.io/api v0.25.2 h1:v6G8RyFcwf0HR5jQGIAYlvtRNrxMJQG1xJzaSeVnIS8= +k8s.io/api v0.25.2/go.mod h1:qP1Rn4sCVFwx/xIhe+we2cwBLTXNcheRyYXwajonhy0= +k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.25.2 h1:WbxfAjCx+AeN8Ilp9joWnyJ6xu9OMeS/fsfjK/5zaQs= +k8s.io/apimachinery v0.25.2/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHwA= +k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= +k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= +k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= +k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= +k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/frame/gins/gins_database.go b/frame/gins/gins_database.go index 2e91fd9b6..5fdeb9057 100644 --- a/frame/gins/gins_database.go +++ b/frame/gins/gins_database.go @@ -59,7 +59,7 @@ func Database(name ...string) gdb.DB { if fileConfig, ok := Config().GetAdapter().(*gcfg.AdapterFile); ok { if _, err = fileConfig.GetFilePath(); err != nil { panic(gerror.WrapCode(gcode.CodeMissingConfiguration, err, - `configuration not found, did you miss the configuration file or the misspell the configuration file name`, + `configuration not found, did you miss the configuration file or misspell the configuration file name`, )) } } diff --git a/frame/gins/gins_z_unit_database_test.go b/frame/gins/gins_z_unit_database_test.go index 3fc466de6..bd18b541c 100644 --- a/frame/gins/gins_z_unit_database_test.go +++ b/frame/gins/gins_z_unit_database_test.go @@ -41,15 +41,16 @@ func Test_Database(t *testing.T) { time.Sleep(500 * time.Millisecond) // fmt.Println("gins Test_Database", Config().Get("test")) - - dbDefault := gins.Database() - dbTest := gins.Database("test") + var ( + db = gins.Database() + dbDefault = gins.Database("default") + ) + t.AssertNE(db, nil) t.AssertNE(dbDefault, nil) - t.AssertNE(dbTest, nil) + t.Assert(db.PingMaster(), nil) + t.Assert(db.PingSlave(), nil) t.Assert(dbDefault.PingMaster(), nil) t.Assert(dbDefault.PingSlave(), nil) - t.Assert(dbTest.PingMaster(), nil) - t.Assert(dbTest.PingSlave(), nil) }) } diff --git a/frame/gins/testdata/database/config.toml b/frame/gins/testdata/database/config.toml index 40f310a7d..b9a25bdc4 100644 --- a/frame/gins/testdata/database/config.toml +++ b/frame/gins/testdata/database/config.toml @@ -9,7 +9,7 @@ test = "v=2" user = "root" pass = "12345678" name = "test" - type = "test" + type = "default" role = "master" weight = "1" charset = "utf8" @@ -19,7 +19,7 @@ test = "v=2" user = "root" pass = "12345678" name = "test" - type = "test" + type = "default" role = "master" weight = "1" charset = "utf8" diff --git a/internal/mutex/mutex.go b/internal/mutex/mutex.go index ccb32d037..656981bdb 100644 --- a/internal/mutex/mutex.go +++ b/internal/mutex/mutex.go @@ -7,39 +7,53 @@ // Package mutex provides switch of concurrent safe feature for sync.Mutex. package mutex -import "sync" +import ( + "sync" +) // Mutex is a sync.Mutex with a switch for concurrent safe feature. type Mutex struct { - sync.Mutex - safe bool + // Underlying mutex. + mutex *sync.Mutex } // New creates and returns a new *Mutex. -// The parameter `safe` is used to specify whether using this mutex in concurrent-safety, +// The parameter `safe` is used to specify whether using this mutex in concurrent safety, // which is false in default. func New(safe ...bool) *Mutex { - mu := new(Mutex) - if len(safe) > 0 { - mu.safe = safe[0] - } else { - mu.safe = false - } - return mu + mu := Create(safe...) + return &mu } +// Create creates and returns a new Mutex object. +// The parameter `safe` is used to specify whether using this mutex in concurrent safety, +// which is false in default. +func Create(safe ...bool) Mutex { + if len(safe) > 0 && safe[0] { + return Mutex{ + mutex: new(sync.Mutex), + } + } + return Mutex{} +} + +// IsSafe checks and returns whether current mutex is in concurrent-safe usage. func (mu *Mutex) IsSafe() bool { - return mu.safe + return mu.mutex != nil } +// Lock locks mutex for writing. +// It does nothing if it is not in concurrent-safe usage. func (mu *Mutex) Lock() { - if mu.safe { - mu.Mutex.Lock() + if mu.mutex != nil { + mu.mutex.Lock() } } +// Unlock unlocks mutex for writing. +// It does nothing if it is not in concurrent-safe usage. func (mu *Mutex) Unlock() { - if mu.safe { - mu.Mutex.Unlock() + if mu.mutex != nil { + mu.mutex.Unlock() } } diff --git a/internal/rwmutex/rwmutex.go b/internal/rwmutex/rwmutex.go index 0d16fb7d3..17a67fec4 100644 --- a/internal/rwmutex/rwmutex.go +++ b/internal/rwmutex/rwmutex.go @@ -7,13 +7,16 @@ // Package rwmutex provides switch of concurrent safety feature for sync.RWMutex. package rwmutex -import "sync" +import ( + "sync" +) // RWMutex is a sync.RWMutex with a switch for concurrent safe feature. // If its attribute *sync.RWMutex is not nil, it means it's in concurrent safety usage. // Its attribute *sync.RWMutex is nil in default, which makes this struct mush lightweight. type RWMutex struct { - *sync.RWMutex + // Underlying mutex. + mutex *sync.RWMutex } // New creates and returns a new *RWMutex. @@ -28,46 +31,47 @@ func New(safe ...bool) *RWMutex { // The parameter `safe` is used to specify whether using this mutex in concurrent safety, // which is false in default. func Create(safe ...bool) RWMutex { - mu := RWMutex{} if len(safe) > 0 && safe[0] { - mu.RWMutex = new(sync.RWMutex) + return RWMutex{ + mutex: new(sync.RWMutex), + } } - return mu + return RWMutex{} } // IsSafe checks and returns whether current mutex is in concurrent-safe usage. func (mu *RWMutex) IsSafe() bool { - return mu.RWMutex != nil + return mu.mutex != nil } // Lock locks mutex for writing. // It does nothing if it is not in concurrent-safe usage. func (mu *RWMutex) Lock() { - if mu.RWMutex != nil { - mu.RWMutex.Lock() + if mu.mutex != nil { + mu.mutex.Lock() } } // Unlock unlocks mutex for writing. // It does nothing if it is not in concurrent-safe usage. func (mu *RWMutex) Unlock() { - if mu.RWMutex != nil { - mu.RWMutex.Unlock() + if mu.mutex != nil { + mu.mutex.Unlock() } } // RLock locks mutex for reading. // It does nothing if it is not in concurrent-safe usage. func (mu *RWMutex) RLock() { - if mu.RWMutex != nil { - mu.RWMutex.RLock() + if mu.mutex != nil { + mu.mutex.RLock() } } // RUnlock unlocks mutex for reading. // It does nothing if it is not in concurrent-safe usage. func (mu *RWMutex) RUnlock() { - if mu.RWMutex != nil { - mu.RWMutex.RUnlock() + if mu.mutex != nil { + mu.mutex.RUnlock() } } diff --git a/os/gcfg/gcfg.go b/os/gcfg/gcfg.go index 5dd791f99..c32f00d97 100644 --- a/os/gcfg/gcfg.go +++ b/os/gcfg/gcfg.go @@ -85,11 +85,7 @@ func (c *Config) GetAdapter() Adapter { // It returns true if configuration file is present in default AdapterFile, or else false. // Note that this function does not return error as it just does simply check for backend configuration service. func (c *Config) Available(ctx context.Context, resource ...string) (ok bool) { - var usedResource string - if len(resource) > 0 { - usedResource = resource[0] - } - return c.adapter.Available(ctx, usedResource) + return c.adapter.Available(ctx, resource...) } // Get retrieves and returns value by specified `pattern`. diff --git a/os/gcfg/gcfg_adaper.go b/os/gcfg/gcfg_adaper.go index 033b0e8eb..50fdd6b90 100644 --- a/os/gcfg/gcfg_adaper.go +++ b/os/gcfg/gcfg_adaper.go @@ -15,7 +15,7 @@ type Adapter interface { // // Note that this function does not return error as it just does simply check for // backend configuration service. - Available(ctx context.Context, resource string) (ok bool) + Available(ctx context.Context, resource ...string) (ok bool) // Get retrieves and returns value by specified `pattern` in current resource. // Pattern like: diff --git a/os/gcfg/gcfg_adapter_file.go b/os/gcfg/gcfg_adapter_file.go index dfb2e9e19..a33d3775e 100644 --- a/os/gcfg/gcfg_adapter_file.go +++ b/os/gcfg/gcfg_adapter_file.go @@ -20,6 +20,7 @@ import ( "github.com/gogf/gf/v2/os/gfsnotify" "github.com/gogf/gf/v2/os/gres" "github.com/gogf/gf/v2/util/gmode" + "github.com/gogf/gf/v2/util/gutil" ) type AdapterFile struct { @@ -197,16 +198,14 @@ func (c *AdapterFile) Dump() { } // Available checks and returns whether configuration of given `file` is available. -func (c *AdapterFile) Available(ctx context.Context, fileName string) bool { - if fileName == "" { - fileName = c.defaultName - } +func (c *AdapterFile) Available(ctx context.Context, fileName ...string) bool { + checkFileName := gutil.GetOrDefaultStr(c.defaultName, fileName...) // Custom configuration content exists. - if c.GetContent(fileName) != "" { + if c.GetContent(checkFileName) != "" { return true } // Configuration file exists in system path. - if path, _ := c.GetFilePath(fileName); path != "" { + if path, _ := c.GetFilePath(checkFileName); path != "" { return true } return false @@ -260,7 +259,7 @@ func (c *AdapterFile) getJson(fileName ...string) (configJson *gjson.Json, err e } } // Note that the underlying configuration json object operations are concurrent safe. - dataType := gfile.ExtName(filePath) + dataType := gjson.ContentType(gfile.ExtName(filePath)) if gjson.IsValidDataType(dataType) && !isFromConfigContent { configJson, err = gjson.LoadContentType(dataType, content, true) } else { diff --git a/os/gcron/gcron_z_unit_test.go b/os/gcron/gcron_z_unit_test.go index ca4f70d12..47a743b5f 100644 --- a/os/gcron/gcron_z_unit_test.go +++ b/os/gcron/gcron_z_unit_test.go @@ -88,11 +88,6 @@ func TestCron_Remove(t *testing.T) { } func TestCron_Add_FixedPattern(t *testing.T) { - //debug := utils.IsDebugEnabled() - //utils.SetDebugEnabled(true) - //defer func() { - // utils.SetDebugEnabled(debug) - //}() for i := 0; i < 5; i++ { doTestCronAddFixedPattern(t) } diff --git a/os/gctx/gctx.go b/os/gctx/gctx.go index e9df625db..62032b362 100644 --- a/os/gctx/gctx.go +++ b/os/gctx/gctx.go @@ -12,9 +12,10 @@ import ( "os" "strings" - "github.com/gogf/gf/v2/net/gtrace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" + + "github.com/gogf/gf/v2/net/gtrace" ) type ( diff --git a/os/gstructs/gstructs.go b/os/gstructs/gstructs.go index b44cbccac..09f911f2a 100644 --- a/os/gstructs/gstructs.go +++ b/os/gstructs/gstructs.go @@ -36,7 +36,7 @@ type FieldsInput struct { // RecursiveOption specifies the way retrieving the fields recursively if the attribute // is an embedded struct. It is RecursiveOptionNone in default. - RecursiveOption int + RecursiveOption RecursiveOption } // FieldMapInput is the input parameter struct type for function FieldMap. @@ -50,11 +50,13 @@ type FieldMapInput struct { // RecursiveOption specifies the way retrieving the fields recursively if the attribute // is an embedded struct. It is RecursiveOptionNone in default. - RecursiveOption int + RecursiveOption RecursiveOption } +type RecursiveOption int + const ( - RecursiveOptionNone = 0 // No recursively retrieving fields as map if the field is an embedded struct. - RecursiveOptionEmbedded = 1 // Recursively retrieving fields as map if the field is an embedded struct. - RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag. + RecursiveOptionNone RecursiveOption = 0 // No recursively retrieving fields as map if the field is an embedded struct. + RecursiveOptionEmbedded RecursiveOption = 1 // Recursively retrieving fields as map if the field is an embedded struct. + RecursiveOptionEmbeddedNoTag RecursiveOption = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag. ) diff --git a/util/gutil/gutil_default.go b/util/gutil/gutil_default.go new file mode 100644 index 000000000..881b03744 --- /dev/null +++ b/util/gutil/gutil_default.go @@ -0,0 +1,27 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gutil + +// GetOrDefaultStr checks and returns value according whether parameter `param` available. +// It returns `param[0]` if it is available, or else it returns `def`. +func GetOrDefaultStr(def string, param ...string) string { + value := def + if len(param) > 0 && param[0] != "" { + value = param[0] + } + return value +} + +// GetOrDefaultAny checks and returns value according whether parameter `param` available. +// It returns `param[0]` if it is available, or else it returns `def`. +func GetOrDefaultAny(def interface{}, param ...interface{}) interface{} { + value := def + if len(param) > 0 && param[0] != "" { + value = param[0] + } + return value +} diff --git a/util/gutil/gutil_z_unit_test.go b/util/gutil/gutil_z_unit_test.go index d36983bdf..58adc32f5 100755 --- a/util/gutil/gutil_z_unit_test.go +++ b/util/gutil/gutil_z_unit_test.go @@ -8,10 +8,11 @@ package gutil_test import ( "context" - "github.com/gogf/gf/v2/errors/gerror" "reflect" "testing" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/test/gtest" "github.com/gogf/gf/v2/util/gutil" @@ -196,3 +197,19 @@ func TestListToMapByKey(t *testing.T) { t.Assert(gutil.ListToMapByKey(listMap, "key1"), "{\"1\":{\"key1\":1,\"key2\":2}}") }) } + +func Test_GetOrDefaultStr(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gutil.GetOrDefaultStr("a", "b"), "b") + t.Assert(gutil.GetOrDefaultStr("a", "b", "c"), "b") + t.Assert(gutil.GetOrDefaultStr("a"), "a") + }) +} + +func Test_GetOrDefaultAny(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(gutil.GetOrDefaultAny("a", "b"), "b") + t.Assert(gutil.GetOrDefaultAny("a", "b", "c"), "b") + t.Assert(gutil.GetOrDefaultAny("a"), "a") + }) +} diff --git a/util/gvalid/gvalid.go b/util/gvalid/gvalid.go index 80373104f..f276ac67f 100644 --- a/util/gvalid/gvalid.go +++ b/util/gvalid/gvalid.go @@ -43,144 +43,21 @@ const ( internalDefaultRuleName = "__default__" // default rule name for i18n error message format if no i18n message found for specified error rule. ruleMessagePrefixForI18n = "gf.gvalid.rule." // prefix string for each rule configuration in i18n content. noValidationTagName = "nv" // no validation tag name for struct attribute. + ruleNameRegex = "regex" // the name for rule "regex" + ruleNameNotRegex = "not-regex" // the name for rule "not-regex" + ruleNameForeach = "foreach" // the name for rule "foreach" ruleNameBail = "bail" // the name for rule "bail" ruleNameCi = "ci" // the name for rule "ci" emptyJsonArrayStr = "[]" // Empty json string for array type. emptyJsonObjectStr = "{}" // Empty json string for object type. + requiredRulesPrefix = "required" // requiredRulesPrefix specifies the rule prefix that must be validated even the value is empty (nil or empty). ) var ( - // allSupportedRules defines all supported rules that is used for quick checks. - // Refer to Laravel validation: - // https://laravel.com/docs/5.5/validation#available-validation-rules - // https://learnku.com/docs/laravel/5.4/validation - allSupportedRules = map[string]struct{}{ - "required": {}, // format: required brief: Required. - "required-if": {}, // format: required-if:field,value,... brief: Required unless all given field and its value are equal. - "required-unless": {}, // format: required-unless:field,value,... brief: Required unless all given field and its value are not equal. - "required-with": {}, // format: required-with:field1,field2,... brief: Required if any of given fields are not empty. - "required-with-all": {}, // format: required-with-all:field1,field2,... brief: Required if all given fields are not empty. - "required-without": {}, // format: required-without:field1,field2,... brief: Required if any of given fields are empty. - "required-without-all": {}, // format: required-without-all:field1,field2,...brief: Required if all given fields are empty. - "bail": {}, // format: bail brief: Stop validating when this field's validation failed. - "ci": {}, // format: ci brief: Case-Insensitive configuration for those rules that need value comparison like: same, different, in, not-in, etc. - "date": {}, // format: date brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02 - "datetime": {}, // format: datetime brief: Standard datetime, like: 2006-01-02 12:00:00 - "date-format": {}, // format: date-format:format brief: Custom date format. - "email": {}, // format: email brief: Email address. - "phone": {}, // format: phone brief: Phone number. - "phone-loose": {}, // format: phone-loose brief: Loose phone number validation. - "telephone": {}, // format: telephone brief: Telephone number, like: "XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX" - "passport": {}, // format: passport brief: Universal passport format rule: Starting with letter, containing only numbers or underscores, length between 6 and 18 - "password": {}, // format: password brief: Universal password format rule1: Containing any visible chars, length between 6 and 18. - "password2": {}, // format: password2 brief: Universal password format rule2: Must meet password rule1, must contain lower and upper letters and numbers. - "password3": {}, // format: password3 brief: Universal password format rule3: Must meet password rule1, must contain lower and upper letters, numbers and special chars. - "postcode": {}, // format: postcode brief: Postcode number. - "resident-id": {}, // format: resident-id brief: Resident id number. - "bank-card": {}, // format: bank-card brief: Bank card number. - "qq": {}, // format: qq brief: Tencent QQ number. - "ip": {}, // format: ip brief: IPv4/IPv6. - "ipv4": {}, // format: ipv4 brief: IPv4. - "ipv6": {}, // format: ipv6 brief: IPv6. - "mac": {}, // format: mac brief: MAC. - "url": {}, // format: url brief: URL. - "domain": {}, // format: domain brief: Domain. - "length": {}, // format: length:min,max brief: Length between :min and :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. - "min-length": {}, // format: min-length:min brief: Length is equal or greater than :min. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. - "max-length": {}, // format: max-length:max brief: Length is equal or lesser than :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. - "size": {}, // format: size:size brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. - "between": {}, // format: between:min,max brief: Range between :min and :max. It supports both integer and float. - "min": {}, // format: min:min brief: Equal or greater than :min. It supports both integer and float. - "max": {}, // format: max:max brief: Equal or lesser than :max. It supports both integer and float. - "json": {}, // format: json brief: JSON. - "integer": {}, // format: integer brief: Integer. - "float": {}, // format: float brief: Float. Note that an integer is actually a float number. - "boolean": {}, // format: boolean brief: Boolean(1,true,on,yes:true | 0,false,off,no,"":false) - "same": {}, // format: same:field brief: Value should be the same as value of field. - "different": {}, // format: different:field brief: Value should be different from value of field. - "in": {}, // format: in:value1,value2,... brief: Value should be in: value1,value2,... - "not-in": {}, // format: not-in:value1,value2,... brief: Value should not be in: value1,value2,... - "regex": {}, // format: regex:pattern brief: Value should match custom regular expression pattern. - } - - // defaultMessages is the default error messages. + // defaultErrorMessages is the default error messages. // Note that these messages are synchronized from ./i18n/en/validation.toml . - defaultMessages = map[string]string{ - "required": "The {attribute} field is required", - "required-if": "The {attribute} field is required", - "required-unless": "The {attribute} field is required", - "required-with": "The {attribute} field is required", - "required-with-all": "The {attribute} field is required", - "required-without": "The {attribute} field is required", - "required-without-all": "The {attribute} field is required", - "date": "The {attribute} value `{value}` is not a valid date", - "datetime": "The {attribute} value `{value}` is not a valid datetime", - "date-format": "The {attribute} value `{value}` does not match the format: {pattern}", - "email": "The {attribute} value `{value}` is not a valid email address", - "phone": "The {attribute} value `{value}` is not a valid phone number", - "telephone": "The {attribute} value `{value}` is not a valid telephone number", - "passport": "The {attribute} value `{value}` is not a valid passport format", - "password": "The {attribute} value `{value}` is not a valid password format", - "password2": "The {attribute} value `{value}` is not a valid password format", - "password3": "The {attribute} value `{value}` is not a valid password format", - "postcode": "The {attribute} value `{value}` is not a valid postcode format", - "resident-id": "The {attribute} value `{value}` is not a valid resident id number", - "bank-card": "The {attribute} value `{value}` is not a valid bank card number", - "qq": "The {attribute} value `{value}` is not a valid QQ number", - "ip": "The {attribute} value `{value}` is not a valid IP address", - "ipv4": "The {attribute} value `{value}` is not a valid IPv4 address", - "ipv6": "The {attribute} value `{value}` is not a valid IPv6 address", - "mac": "The {attribute} value `{value}` is not a valid MAC address", - "url": "The {attribute} value `{value}` is not a valid URL address", - "domain": "The {attribute} value `{value}` is not a valid domain format", - "length": "The {attribute} value `{value}` length must be between {min} and {max}", - "min-length": "The {attribute} value `{value}` length must be equal or greater than {min}", - "max-length": "The {attribute} value `{value}` length must be equal or lesser than {max}", - "size": "The {attribute} value `{value}` length must be {size}", - "between": "The {attribute} value `{value}` must be between {min} and {max}", - "min": "The {attribute} value `{value}` must be equal or greater than {min}", - "max": "The {attribute} value `{value}` must be equal or lesser than {max}", - "json": "The {attribute} value `{value}` is not a valid JSON string", - "xml": "The {attribute} value `{value}` is not a valid XML string", - "array": "The {attribute} value `{value}` is not an array", - "integer": "The {attribute} value `{value}` is not an integer", - "boolean": "The {attribute} value `{value}` field must be true or false", - "same": "The {attribute} value `{value}` must be the same as field {pattern}", - "different": "The {attribute} value `{value}` must be different from field {pattern}", - "in": "The {attribute} value `{value}` is not in acceptable range: {pattern}", - "not-in": "The {attribute} value `{value}` must not be in range: {pattern}", - "regex": "The {attribute} value `{value}` must be in regex of: {pattern}", - internalDefaultRuleName: "The {attribute} value `{value}` is invalid", - } - - // mustCheckRulesEvenValueEmpty specifies some rules that must be validated - // even the value is empty (nil or empty). - mustCheckRulesEvenValueEmpty = map[string]struct{}{ - "required": {}, - "required-if": {}, - "required-unless": {}, - "required-with": {}, - "required-with-all": {}, - "required-without": {}, - "required-without-all": {}, - //"same": {}, - //"different": {}, - //"in": {}, - //"not-in": {}, - //"regex": {}, - } - - // boolMap defines the boolean values. - boolMap = map[string]struct{}{ - "1": {}, - "true": {}, - "on": {}, - "yes": {}, - "": {}, - "0": {}, - "false": {}, - "off": {}, - "no": {}, + defaultErrorMessages = map[string]string{ + internalDefaultRuleName: "The {field} value `{value}` is invalid", } structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array. @@ -196,11 +73,12 @@ var ( // which is compiled just once and of repeatable usage. ruleRegex, _ = regexp.Compile(singleRulePattern) - // markedRuleMap defines all rules that are just marked rules which have neither functional meaning + // decorativeRuleMap defines all rules that are just marked rules which have neither functional meaning // nor error messages. - markedRuleMap = map[string]bool{ - ruleNameBail: true, - ruleNameCi: true, + decorativeRuleMap = map[string]bool{ + ruleNameForeach: true, + ruleNameBail: true, + ruleNameCi: true, } ) diff --git a/util/gvalid/gvalid_custom_rule.go b/util/gvalid/gvalid_register.go similarity index 100% rename from util/gvalid/gvalid_custom_rule.go rename to util/gvalid/gvalid_register.go diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index 944774f3c..036197819 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -14,6 +14,7 @@ import ( "github.com/gogf/gf/v2/i18n/gi18n" "github.com/gogf/gf/v2/internal/reflection" "github.com/gogf/gf/v2/internal/utils" + "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" ) @@ -27,6 +28,7 @@ type Validator struct { ruleFuncMap map[string]RuleFunc // ruleFuncMap stores custom rule functions for current Validator. useAssocInsteadOfObjectAttributes bool // Using `assoc` as its validation source instead of attribute values from `Object`. bail bool // Stop validation after the first validation error. + foreach bool // It tells the next validation using current value as an array and validates each of its element. caseInsensitive bool // Case-Insensitive configuration for those rules that need value comparison. } @@ -106,6 +108,14 @@ func (v *Validator) Bail() *Validator { return newValidator } +// Foreach tells the next validation using current value as an array and validates each of its element. +// Note that this decorating rule takes effect just once for next validation rule, specially for single value validation. +func (v *Validator) Foreach() *Validator { + newValidator := v.Clone() + newValidator.foreach = true + return newValidator +} + // Ci sets the mark for Case-Insensitive for those rules that need value comparison. func (v *Validator) Ci() *Validator { newValidator := v.Clone() @@ -177,11 +187,24 @@ func (v *Validator) RuleFuncMap(m map[string]RuleFunc) *Validator { return newValidator } -// getRuleFunc retrieves and returns the custom rule function for specified rule. -func (v *Validator) getRuleFunc(rule string) RuleFunc { +// getCustomRuleFunc retrieves and returns the custom rule function for specified rule. +func (v *Validator) getCustomRuleFunc(rule string) RuleFunc { ruleFunc := v.ruleFuncMap[rule] if ruleFunc == nil { ruleFunc = customRuleFuncMap[rule] } return ruleFunc } + +// checkRuleRequired checks and returns whether the given `rule` is required even it is nil or empty. +func (v *Validator) checkRuleRequired(rule string) bool { + // Default required rules. + if gstr.HasPrefix(rule, requiredRulesPrefix) { + return true + } + // All custom validation rules are required rules. + if _, ok := customRuleFuncMap[rule]; ok { + return true + } + return false +} diff --git a/util/gvalid/gvalid_validator_check_map.go b/util/gvalid/gvalid_validator_check_map.go index f774f38e5..8a95805cf 100644 --- a/util/gvalid/gvalid_validator_check_map.go +++ b/util/gvalid/gvalid_validator_check_map.go @@ -145,9 +145,7 @@ func (v *Validator) doCheckMap(ctx context.Context, params interface{}) Error { required := false // rule => error for ruleKey := range errorItem { - // Default required rules. - if _, ok := mustCheckRulesEvenValueEmpty[ruleKey]; ok { - required = true + if required = v.checkRuleRequired(ruleKey); required { break } } diff --git a/util/gvalid/gvalid_validator_check_struct.go b/util/gvalid/gvalid_validator_check_struct.go index 5b01b66a8..25bf154cc 100644 --- a/util/gvalid/gvalid_validator_check_struct.go +++ b/util/gvalid/gvalid_validator_check_struct.go @@ -311,14 +311,7 @@ func (v *Validator) doCheckStruct(ctx context.Context, object interface{}) Error required := false // rule => error for ruleKey := range errorItem { - // Default required rules. - if _, ok := mustCheckRulesEvenValueEmpty[ruleKey]; ok { - required = true - break - } - // All custom validation rules are required rules. - if _, ok := customRuleFuncMap[ruleKey]; ok { - required = true + if required = v.checkRuleRequired(ruleKey); required { break } } diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index ebfb6d01f..96eb8bcb4 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -10,29 +10,18 @@ import ( "context" "errors" "reflect" - "strconv" "strings" - "time" "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/internal/json" - "github.com/gogf/gf/v2/net/gipv4" - "github.com/gogf/gf/v2/net/gipv6" - "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" - "github.com/gogf/gf/v2/util/gutil" + "github.com/gogf/gf/v2/util/gvalid/internal/builtin" ) -type iTime interface { - Date() (year int, month time.Month, day int) - IsZero() bool -} - type doCheckValueInput struct { Name string // Name specifies the name of parameter `value`. Value interface{} // Value specifies the value for the rules to be validated. @@ -72,9 +61,25 @@ func (v *Validator) doCheckValue(ctx context.Context, in doCheckValueInput) Erro ruleItems := strings.Split(strings.TrimSpace(in.Rule), "|") for i := 0; ; { array := strings.Split(ruleItems[i], ":") - _, ok := allSupportedRules[array[0]] - if !ok && v.getRuleFunc(array[0]) == nil { - if i > 0 && ruleItems[i-1][:5] == "regex" { + if builtin.GetRule(array[0]) == nil && v.getCustomRuleFunc(array[0]) == nil { + // ============================ SPECIAL ============================ + // Special `regex` and `not-regex` rules. + // Merge the regex pattern if there are special chars, like ':', '|', in pattern. + // ============================ SPECIAL ============================ + var ( + ruleNameRegexLengthMatch bool + ruleNameNotRegexLengthMatch bool + ) + if i > 0 { + ruleItem := ruleItems[i-1] + if len(ruleItem) >= len(ruleNameRegex) && ruleItem[:len(ruleNameRegex)] == ruleNameRegex { + ruleNameRegexLengthMatch = true + } + if len(ruleItem) >= len(ruleNameNotRegex) && ruleItem[:len(ruleNameNotRegex)] == ruleNameNotRegex { + ruleNameNotRegexLengthMatch = true + } + } + if i > 0 && (ruleNameRegexLengthMatch || ruleNameNotRegexLengthMatch) { ruleItems[i-1] += "|" + ruleItems[i] ruleItems = append(ruleItems[:i], ruleItems[i+1:]...) } else { @@ -92,28 +97,29 @@ func (v *Validator) doCheckValue(ctx context.Context, in doCheckValueInput) Erro } var ( hasBailRule = v.bail + hasForeachRule = v.foreach hasCaseInsensitive = v.caseInsensitive ) for index := 0; index < len(ruleItems); { var ( - err error - match = false // whether this rule is matched(has no error) - results = ruleRegex.FindStringSubmatch(ruleItems[index]) // split single rule. - ruleKey = gstr.Trim(results[1]) // rule key like "max" in rule "max: 6" - rulePattern = gstr.Trim(results[2]) // rule pattern is like "6" in rule:"max:6" - customRuleFunc RuleFunc + err error + results = ruleRegex.FindStringSubmatch(ruleItems[index]) // split single rule. + ruleKey = gstr.Trim(results[1]) // rule key like "max" in rule "max: 6" + rulePattern = gstr.Trim(results[2]) // rule pattern is like "6" in rule:"max:6" ) if !hasBailRule && ruleKey == ruleNameBail { hasBailRule = true } - + if !hasForeachRule && ruleKey == ruleNameForeach { + hasForeachRule = true + } if !hasCaseInsensitive && ruleKey == ruleNameCi { hasCaseInsensitive = true } // Ignore logic executing for marked rules. - if markedRuleMap[ruleKey] { + if decorativeRuleMap[ruleKey] { index++ continue } @@ -122,85 +128,87 @@ func (v *Validator) doCheckValue(ctx context.Context, in doCheckValueInput) Erro customMsgMap[ruleKey] = strings.TrimSpace(msgArray[index]) } - // =========================================================================================== - // Custom rule handling - // =========================================================================================== - // 1. It firstly checks and uses the custom registered rules functions in the current Validator. - // 2. It secondly checks and uses the globally registered rules functions. - // 3. It finally checks and uses the build-in rules functions. - customRuleFunc = v.getRuleFunc(ruleKey) - if customRuleFunc != nil { - // It checks custom validation rules with most priority. - message := v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - if err = customRuleFunc(ctx, RuleFuncInput{ - Rule: ruleItems[index], - Message: message, - Value: gvar.New(in.Value), - Data: gvar.New(in.DataRaw), - }); err != nil { - match = false - // The error should have stack info to indicate the error position. - if !gerror.HasStack(err) { - err = gerror.NewCodeSkip(gcode.CodeValidationFailed, 1, err.Error()) + var ( + message = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) + customRuleFunc = v.getCustomRuleFunc(ruleKey) + builtinRule = builtin.GetRule(ruleKey) + foreachValues = []interface{}{in.Value} + ) + if hasForeachRule { + // As it marks `foreach`, so it converts the value to slice. + foreachValues = gconv.Interfaces(in.Value) + // Reset `foreach` rule as it only takes effect just once for next rule. + hasForeachRule = false + } + + for _, value := range foreachValues { + switch { + // Custom validation rules. + case customRuleFunc != nil: + if err = customRuleFunc(ctx, RuleFuncInput{ + Rule: ruleItems[index], + Message: message, + Value: gvar.New(value), + Data: gvar.New(in.DataRaw), + }); err != nil { + // The error should have stack info to indicate the error position. + if !gerror.HasStack(err) { + err = gerror.NewCodeSkip(gcode.CodeValidationFailed, 1, err.Error()) + } } + + // Builtin validation rules. + case customRuleFunc == nil && builtinRule != nil: + err = builtinRule.Run(builtin.RunInput{ + RuleKey: ruleKey, + RulePattern: rulePattern, + Field: in.Name, + Value: gvar.New(value), + Data: gvar.New(in.DataRaw), + Message: message, + Option: builtin.RunOption{ + CaseInsensitive: hasCaseInsensitive, + }, + }) + + default: + // It never comes across here. + } + + // Error handling. + if err != nil { // The error should have error code that is `gcode.CodeValidationFailed`. if gerror.Code(err) == gcode.CodeNil { if e, ok := err.(*gerror.Error); ok { e.SetCode(gcode.CodeValidationFailed) } } + + // Error variable replacement for error message. + if !gerror.HasStack(err) { + var s string + s = gstr.ReplaceByMap(err.Error(), map[string]string{ + "{field}": in.Name, // Field name of the `value`. + "{value}": gconv.String(value), // Current validating value. + "{pattern}": rulePattern, // The variable part of the rule. + "{attribute}": in.Name, // The same as `{field}`. It is deprecated. + }) + s, _ = gregex.ReplaceString(`\s{2,}`, ` `, s) + err = errors.New(s) + } ruleErrorMap[ruleKey] = err - } else { - match = true - } - } else { - // It checks build-in validation rules if there's no custom rule. - match, err = v.doCheckSingleBuildInRules( - ctx, - doCheckBuildInRulesInput{ - Index: index, - Value: in.Value, - RuleKey: ruleKey, - RulePattern: rulePattern, - RuleItems: ruleItems, - DataMap: in.DataMap, - CustomMsgMap: customMsgMap, - CaseInsensitive: hasCaseInsensitive, - }, - ) - if !match && err != nil { - ruleErrorMap[ruleKey] = err - } - } - // Error message handling. - if !match { - // It does nothing if the error message for this rule - // is already set in previous validation. - if _, ok := ruleErrorMap[ruleKey]; !ok { - ruleErrorMap[ruleKey] = errors.New(v.getErrorMessageByRule(ctx, ruleKey, customMsgMap)) - } - - // Error variable replacement for error message. - if err = ruleErrorMap[ruleKey]; !gerror.HasStack(err) { - var s string - s = gstr.ReplaceByMap(err.Error(), map[string]string{ - "{value}": gconv.String(in.Value), - "{pattern}": rulePattern, - "{attribute}": in.Name, - }) - s, _ = gregex.ReplaceString(`\s{2,}`, ` `, s) - ruleErrorMap[ruleKey] = errors.New(s) - } - - // If it is with error and there's bail rule, - // it then does not continue validating for left rules. - if hasBailRule { - break + // If it is with error and there's bail rule, + // it then does not continue validating for left rules. + if hasBailRule { + goto CheckDone + } } } index++ } + +CheckDone: if len(ruleErrorMap) > 0 { return newValidationError( gcode.CodeValidationFailed, @@ -213,330 +221,6 @@ func (v *Validator) doCheckValue(ctx context.Context, in doCheckValueInput) Erro return nil } -type doCheckBuildInRulesInput struct { - Index int // Index of RuleKey in RuleItems. - Value interface{} // Value to be validated. - RuleKey string // RuleKey is like the "max" in rule "max: 6" - RulePattern string // RulePattern is like "6" in rule:"max:6" - RuleItems []string // RuleItems are all the rules that should be validated on single field, like: []string{"required", "min:1"} - DataMap map[string]interface{} // Parameter map. - CustomMsgMap map[string]string // Custom error message map. - CaseInsensitive bool // Case-Insensitive comparison. -} - -func (v *Validator) doCheckSingleBuildInRules(ctx context.Context, in doCheckBuildInRulesInput) (match bool, err error) { - valueStr := gconv.String(in.Value) - switch in.RuleKey { - // Required rules. - case - "required", - "required-if", - "required-unless", - "required-with", - "required-with-all", - "required-without", - "required-without-all": - match = v.checkRequired(checkRequiredInput{ - Value: in.Value, - RuleKey: in.RuleKey, - RulePattern: in.RulePattern, - DataMap: in.DataMap, - CaseInsensitive: in.CaseInsensitive, - }) - - // Length rules. - // It also supports length of unicode string. - case - "length", - "min-length", - "max-length", - "size": - if msg := v.checkLength(ctx, valueStr, in.RuleKey, in.RulePattern, in.CustomMsgMap); msg != "" { - return match, errors.New(msg) - } else { - match = true - } - - // Range rules. - case - "min", - "max", - "between": - if msg := v.checkRange(ctx, valueStr, in.RuleKey, in.RulePattern, in.CustomMsgMap); msg != "" { - return match, errors.New(msg) - } else { - match = true - } - - // Custom regular expression. - case "regex": - // It here should check the rule as there might be special char '|' in it. - for i := in.Index + 1; i < len(in.RuleItems); i++ { - if !gregex.IsMatchString(singleRulePattern, in.RuleItems[i]) { - in.RulePattern += "|" + in.RuleItems[i] - in.Index++ - } - } - match = gregex.IsMatchString(in.RulePattern, valueStr) - - // Date rules. - case "date": - // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. - if value, ok := in.Value.(iTime); ok { - return !value.IsZero(), nil - } - match = gregex.IsMatchString(`\d{4}[\.\-\_/]{0,1}\d{2}[\.\-\_/]{0,1}\d{2}`, valueStr) - - // Datetime rule. - case "datetime": - // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. - if value, ok := in.Value.(iTime); ok { - return !value.IsZero(), nil - } - if _, err = gtime.StrToTimeFormat(valueStr, `Y-m-d H:i:s`); err == nil { - match = true - } - - // Date rule with specified format. - case "date-format": - // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. - if value, ok := in.Value.(iTime); ok { - return !value.IsZero(), nil - } - if _, err = gtime.StrToTimeFormat(valueStr, in.RulePattern); err == nil { - match = true - } else { - var ( - msg string - ) - msg = v.getErrorMessageByRule(ctx, in.RuleKey, in.CustomMsgMap) - return match, errors.New(msg) - } - - // Values of two fields should be equal as string. - case "same": - _, foundValue := gutil.MapPossibleItemByKey(in.DataMap, in.RulePattern) - if foundValue != nil { - if in.CaseInsensitive { - match = strings.EqualFold(valueStr, gconv.String(foundValue)) - } else { - match = strings.Compare(valueStr, gconv.String(foundValue)) == 0 - } - } - if !match { - var msg string - msg = v.getErrorMessageByRule(ctx, in.RuleKey, in.CustomMsgMap) - return match, errors.New(msg) - } - - // Values of two fields should not be equal as string. - case "different": - match = true - _, foundValue := gutil.MapPossibleItemByKey(in.DataMap, in.RulePattern) - if foundValue != nil { - if in.CaseInsensitive { - match = !strings.EqualFold(valueStr, gconv.String(foundValue)) - } else { - match = strings.Compare(valueStr, gconv.String(foundValue)) != 0 - } - } - if !match { - var msg string - msg = v.getErrorMessageByRule(ctx, in.RuleKey, in.CustomMsgMap) - return match, errors.New(msg) - } - - // Field value should be in range of. - case "in": - for _, value := range gstr.SplitAndTrim(in.RulePattern, ",") { - if in.CaseInsensitive { - match = strings.EqualFold(valueStr, strings.TrimSpace(value)) - } else { - match = strings.Compare(valueStr, strings.TrimSpace(value)) == 0 - } - if match { - break - } - } - - // Field value should not be in range of. - case "not-in": - match = true - for _, value := range gstr.SplitAndTrim(in.RulePattern, ",") { - if in.CaseInsensitive { - match = !strings.EqualFold(valueStr, strings.TrimSpace(value)) - } else { - match = strings.Compare(valueStr, strings.TrimSpace(value)) != 0 - } - if !match { - break - } - } - - // Phone format validation. - // 1. China Mobile: - // 134, 135, 136, 137, 138, 139, 150, 151, 152, 157, 158, 159, 182, 183, 184, 187, 188, - // 178(4G), 147(Net); - // 172 - // - // 2. China Unicom: - // 130, 131, 132, 155, 156, 185, 186 ,176(4G), 145(Net), 175 - // - // 3. China Telecom: - // 133, 153, 180, 181, 189, 177(4G) - // - // 4. Satelite: - // 1349 - // - // 5. Virtual: - // 170, 173 - // - // 6. 2018: - // 16x, 19x - case "phone": - match = gregex.IsMatchString(`^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,2,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$`, valueStr) - - // Loose mobile phone number verification(宽松的手机号验证) - // As long as the 11 digit numbers beginning with - // 13, 14, 15, 16, 17, 18, 19 can pass the verification (只要满足 13、14、15、16、17、18、19开头的11位数字都可以通过验证) - case "phone-loose": - match = gregex.IsMatchString(`^1(3|4|5|6|7|8|9)\d{9}$`, valueStr) - - // Telephone number: - // "XXXX-XXXXXXX" - // "XXXX-XXXXXXXX" - // "XXX-XXXXXXX" - // "XXX-XXXXXXXX" - // "XXXXXXX" - // "XXXXXXXX" - case "telephone": - match = gregex.IsMatchString(`^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, valueStr) - - // QQ number: from 10000. - case "qq": - match = gregex.IsMatchString(`^[1-9][0-9]{4,}$`, valueStr) - - // Postcode number. - case "postcode": - match = gregex.IsMatchString(`^\d{6}$`, valueStr) - - // China resident id number. - // - // xxxxxx yyyy MM dd 375 0 十八位 - // xxxxxx yy MM dd 75 0 十五位 - // - // 地区: [1-9]\d{5} - // 年的前两位:(18|19|([23]\d)) 1800-2399 - // 年的后两位:\d{2} - // 月份: ((0[1-9])|(10|11|12)) - // 天数: (([0-2][1-9])|10|20|30|31) 闰年不能禁止29+ - // - // 三位顺序码:\d{3} - // 两位顺序码:\d{2} - // 校验码: [0-9Xx] - // - // 十八位:^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$ - // 十五位:^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$ - // - // 总: - // (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$) - case "resident-id": - match = v.checkResidentId(valueStr) - - // Bank card number using LUHN algorithm. - case "bank-card": - match = v.checkLuHn(valueStr) - - // Universal passport format rule: - // Starting with letter, containing only numbers or underscores, length between 6 and 18. - case "passport": - match = gregex.IsMatchString(`^[a-zA-Z]{1}\w{5,17}$`, valueStr) - - // Universal password format rule1: - // Containing any visible chars, length between 6 and 18. - case "password": - match = gregex.IsMatchString(`^[\w\S]{6,18}$`, valueStr) - - // Universal password format rule2: - // Must meet password rule1, must contain lower and upper letters and numbers. - case "password2": - if gregex.IsMatchString(`^[\w\S]{6,18}$`, valueStr) && - gregex.IsMatchString(`[a-z]+`, valueStr) && - gregex.IsMatchString(`[A-Z]+`, valueStr) && - gregex.IsMatchString(`\d+`, valueStr) { - match = true - } - - // Universal password format rule3: - // Must meet password rule1, must contain lower and upper letters, numbers and special chars. - case "password3": - if gregex.IsMatchString(`^[\w\S]{6,18}$`, valueStr) && - gregex.IsMatchString(`[a-z]+`, valueStr) && - gregex.IsMatchString(`[A-Z]+`, valueStr) && - gregex.IsMatchString(`\d+`, valueStr) && - gregex.IsMatchString(`[^a-zA-Z0-9]+`, valueStr) { - match = true - } - - // Json. - case "json": - if json.Valid([]byte(valueStr)) { - match = true - } - - // Integer. - case "integer": - if _, err = strconv.Atoi(valueStr); err == nil { - match = true - } - - // Float. - case "float": - if _, err = strconv.ParseFloat(valueStr, 10); err == nil { - match = true - } - - // Boolean(1,true,on,yes:true | 0,false,off,no,"":false). - case "boolean": - match = false - if _, ok := boolMap[strings.ToLower(valueStr)]; ok { - match = true - } - - // Email. - case "email": - match = gregex.IsMatchString(`^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)+$`, valueStr) - - // URL - case "url": - match = gregex.IsMatchString(`(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]`, valueStr) - - // Domain - case "domain": - match = gregex.IsMatchString(`^([0-9a-zA-Z][0-9a-zA-Z\-]{0,62}\.)+([a-zA-Z]{0,62})$`, valueStr) - - // IP(IPv4/IPv6). - case "ip": - match = gipv4.Validate(valueStr) || gipv6.Validate(valueStr) - - // IPv4. - case "ipv4": - match = gipv4.Validate(valueStr) - - // IPv6. - case "ipv6": - match = gipv6.Validate(valueStr) - - // MAC. - case "mac": - match = gregex.IsMatchString(`^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, valueStr) - - default: - return match, errors.New("Invalid rule name: " + in.RuleKey) - } - return match, nil -} - type doCheckValueRecursivelyInput struct { Value interface{} // Value to be validated. Type reflect.Type // Struct/map/slice type which to be recursively validated. diff --git a/util/gvalid/gvalid_validator_message.go b/util/gvalid/gvalid_validator_message.go index 30cab76d8..0585e4469 100644 --- a/util/gvalid/gvalid_validator_message.go +++ b/util/gvalid/gvalid_validator_message.go @@ -6,7 +6,11 @@ package gvalid -import "context" +import ( + "context" + + "github.com/gogf/gf/v2/util/gvalid/internal/builtin" +) // getErrorMessageByRule retrieves and returns the error message for specified rule. // It firstly retrieves the message from custom message map, and then checks i18n manager, @@ -21,17 +25,25 @@ func (v *Validator) getErrorMessageByRule(ctx context.Context, ruleKey string, c } return content } + // Retrieve default message according to certain rule. content = v.i18nManager.GetContent(ctx, ruleMessagePrefixForI18n+ruleKey) if content == "" { - content = defaultMessages[ruleKey] + content = defaultErrorMessages[ruleKey] + } + // Builtin rule message. + if content == "" { + if builtinRule := builtin.GetRule(ruleKey); builtinRule != nil { + content = builtinRule.Message() + } } // If there's no configured rule message, it uses default one. if content == "" { content = v.i18nManager.GetContent(ctx, ruleMessagePrefixForI18n+internalDefaultRuleName) - if content == "" { - content = defaultMessages[internalDefaultRuleName] - } + } + // If there's no configured rule message, it uses default one. + if content == "" { + content = defaultErrorMessages[internalDefaultRuleName] } return content } diff --git a/util/gvalid/gvalid_validator_rule_length.go b/util/gvalid/gvalid_validator_rule_length.go deleted file mode 100644 index 7955e5ac2..000000000 --- a/util/gvalid/gvalid_validator_rule_length.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gvalid - -import ( - "context" - "strconv" - "strings" - - "github.com/gogf/gf/v2/util/gconv" -) - -// checkLength checks `value` using length rules. -// The length is calculated using unicode string, which means one chinese character or letter -// both has the length of 1. -func (v *Validator) checkLength(ctx context.Context, value, ruleKey, ruleVal string, customMsgMap map[string]string) string { - var ( - msg = "" - runeArray = gconv.Runes(value) - valueLen = len(runeArray) - ) - switch ruleKey { - case "length": - var ( - min = 0 - max = 0 - array = strings.Split(ruleVal, ",") - ) - if len(array) > 0 { - if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil { - min = v - } - } - if len(array) > 1 { - if v, err := strconv.Atoi(strings.TrimSpace(array[1])); err == nil { - max = v - } - } - if valueLen < min || valueLen > max { - msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - msg = strings.Replace(msg, "{min}", strconv.Itoa(min), -1) - msg = strings.Replace(msg, "{max}", strconv.Itoa(max), -1) - return msg - } - - case "min-length": - min, err := strconv.Atoi(ruleVal) - if valueLen < min || err != nil { - msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - msg = strings.Replace(msg, "{min}", strconv.Itoa(min), -1) - } - - case "max-length": - max, err := strconv.Atoi(ruleVal) - if valueLen > max || err != nil { - msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - msg = strings.Replace(msg, "{max}", strconv.Itoa(max), -1) - } - - case "size": - size, err := strconv.Atoi(ruleVal) - if valueLen != size || err != nil { - msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - msg = strings.Replace(msg, "{size}", strconv.Itoa(size), -1) - } - } - return msg -} diff --git a/util/gvalid/gvalid_validator_rule_range.go b/util/gvalid/gvalid_validator_rule_range.go deleted file mode 100644 index fc8bf3695..000000000 --- a/util/gvalid/gvalid_validator_rule_range.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gvalid - -import ( - "context" - "strconv" - "strings" -) - -// checkRange checks `value` using range rules. -func (v *Validator) checkRange(ctx context.Context, value, ruleKey, ruleVal string, customMsgMap map[string]string) string { - msg := "" - switch ruleKey { - // Value range. - case "between": - array := strings.Split(ruleVal, ",") - min := float64(0) - max := float64(0) - if len(array) > 0 { - if v, err := strconv.ParseFloat(strings.TrimSpace(array[0]), 10); err == nil { - min = v - } - } - if len(array) > 1 { - if v, err := strconv.ParseFloat(strings.TrimSpace(array[1]), 10); err == nil { - max = v - } - } - valueF, err := strconv.ParseFloat(value, 10) - if valueF < min || valueF > max || err != nil { - msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - msg = strings.Replace(msg, "{min}", strconv.FormatFloat(min, 'f', -1, 64), -1) - msg = strings.Replace(msg, "{max}", strconv.FormatFloat(max, 'f', -1, 64), -1) - } - - // Min value. - case "min": - var ( - min, err1 = strconv.ParseFloat(ruleVal, 10) - valueN, err2 = strconv.ParseFloat(value, 10) - ) - if valueN < min || err1 != nil || err2 != nil { - msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - msg = strings.Replace(msg, "{min}", strconv.FormatFloat(min, 'f', -1, 64), -1) - } - - // Max value. - case "max": - var ( - max, err1 = strconv.ParseFloat(ruleVal, 10) - valueN, err2 = strconv.ParseFloat(value, 10) - ) - if valueN > max || err1 != nil || err2 != nil { - msg = v.getErrorMessageByRule(ctx, ruleKey, customMsgMap) - msg = strings.Replace(msg, "{max}", strconv.FormatFloat(max, 'f', -1, 64), -1) - } - } - return msg -} diff --git a/util/gvalid/gvalid_validator_rule_required.go b/util/gvalid/gvalid_validator_rule_required.go deleted file mode 100644 index c09dedf24..000000000 --- a/util/gvalid/gvalid_validator_rule_required.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gvalid - -import ( - "reflect" - "strings" - - "github.com/gogf/gf/v2/internal/empty" - "github.com/gogf/gf/v2/util/gconv" - "github.com/gogf/gf/v2/util/gutil" -) - -type checkRequiredInput struct { - Value interface{} // Value to be validated. - RuleKey string // RuleKey is like the "max" in rule "max: 6" - RulePattern string // RulePattern is like "6" in rule:"max:6" - DataMap map[string]interface{} // Parameter map. - CaseInsensitive bool // Case-Insensitive comparison. -} - -// checkRequired checks `value` using required rules. -// It also supports require checks for `value` of type: slice, map. -func (v *Validator) checkRequired(in checkRequiredInput) bool { - required := false - switch in.RuleKey { - // Required. - case "required": - required = true - - // Required unless all given field and its value are equal. - // Example: required-if: id,1,age,18 - case "required-if": - required = false - var ( - array = strings.Split(in.RulePattern, ",") - foundValue interface{} - ) - // It supports multiple field and value pairs. - if len(array)%2 == 0 { - for i := 0; i < len(array); { - tk := array[i] - tv := array[i+1] - _, foundValue = gutil.MapPossibleItemByKey(in.DataMap, tk) - if in.CaseInsensitive { - required = strings.EqualFold(tv, gconv.String(foundValue)) - } else { - required = strings.Compare(tv, gconv.String(foundValue)) == 0 - } - if required { - break - } - i += 2 - } - } - - // Required unless all given field and its value are not equal. - // Example: required-unless: id,1,age,18 - case "required-unless": - required = true - var ( - array = strings.Split(in.RulePattern, ",") - foundValue interface{} - ) - // It supports multiple field and value pairs. - if len(array)%2 == 0 { - for i := 0; i < len(array); { - tk := array[i] - tv := array[i+1] - _, foundValue = gutil.MapPossibleItemByKey(in.DataMap, tk) - if in.CaseInsensitive { - required = !strings.EqualFold(tv, gconv.String(foundValue)) - } else { - required = strings.Compare(tv, gconv.String(foundValue)) != 0 - } - if !required { - break - } - i += 2 - } - } - - // Required if any of given fields are not empty. - // Example: required-with:id,name - case "required-with": - required = false - var ( - array = strings.Split(in.RulePattern, ",") - foundValue interface{} - ) - for i := 0; i < len(array); i++ { - _, foundValue = gutil.MapPossibleItemByKey(in.DataMap, array[i]) - if !empty.IsEmpty(foundValue) { - required = true - break - } - } - - // Required if all given fields are not empty. - // Example: required-with:id,name - case "required-with-all": - required = true - var ( - array = strings.Split(in.RulePattern, ",") - foundValue interface{} - ) - for i := 0; i < len(array); i++ { - _, foundValue = gutil.MapPossibleItemByKey(in.DataMap, array[i]) - if empty.IsEmpty(foundValue) { - required = false - break - } - } - - // Required if any of given fields are empty. - // Example: required-with:id,name - case "required-without": - required = false - var ( - array = strings.Split(in.RulePattern, ",") - foundValue interface{} - ) - for i := 0; i < len(array); i++ { - _, foundValue = gutil.MapPossibleItemByKey(in.DataMap, array[i]) - if empty.IsEmpty(foundValue) { - required = true - break - } - } - - // Required if all given fields are empty. - // Example: required-with:id,name - case "required-without-all": - required = true - var ( - array = strings.Split(in.RulePattern, ",") - foundValue interface{} - ) - for i := 0; i < len(array); i++ { - _, foundValue = gutil.MapPossibleItemByKey(in.DataMap, array[i]) - if !empty.IsEmpty(foundValue) { - required = false - break - } - } - } - if required { - reflectValue := reflect.ValueOf(in.Value) - for reflectValue.Kind() == reflect.Ptr { - reflectValue = reflectValue.Elem() - } - switch reflectValue.Kind() { - case reflect.String, reflect.Map, reflect.Array, reflect.Slice: - return reflectValue.Len() != 0 - } - return gconv.String(in.Value) != "" - } else { - return true - } -} diff --git a/util/gvalid/gvalid_z_example_feature_rule_test.go b/util/gvalid/gvalid_z_example_feature_rule_test.go index 460fdbf23..4f3c5dae4 100644 --- a/util/gvalid/gvalid_z_example_feature_rule_test.go +++ b/util/gvalid/gvalid_z_example_feature_rule_test.go @@ -9,6 +9,7 @@ package gvalid_test import ( "context" "fmt" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/text/gstr" ) @@ -379,8 +380,8 @@ func Example_Rule_PhoneLoose() { } // Output: - // The PhoneNumber2 value `11578912345` is invalid - // The PhoneNumber4 value `1357891234` is invalid + // The PhoneNumber2 value `11578912345` is not a valid phone number + // The PhoneNumber4 value `1357891234` is not a valid phone number } func Example_Rule_Telephone() { @@ -959,7 +960,7 @@ func Example_Rule_Float() { } // Output: - // The Str value `goframe` is invalid + // The Str value `goframe` is not of valid float type } func Example_Rule_Boolean() { @@ -1102,3 +1103,287 @@ func Example_Rule_Regex() { // The Regex1 value `1234` must be in regex of: [1-9][0-9]{4,14} // The Regex2 value `01234` must be in regex of: [1-9][0-9]{4,14} } + +func Example_Rule_NotRegex() { + type BizReq struct { + Regex1 string `v:"regex:\\d{4}"` + Regex2 string `v:"not-regex:\\d{4}"` + } + var ( + ctx = context.Background() + req = BizReq{ + Regex1: "1234", + Regex2: "1234", + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Print(gstr.Join(err.Strings(), "\n")) + } + + // Output: + // The Regex2 value `1234` should not be in regex of: \d{4} +} + +func Example_Rule_After() { + type BizReq struct { + Time1 string + Time2 string `v:"after:Time1"` + Time3 string `v:"after:Time1"` + } + var ( + ctx = context.Background() + req = BizReq{ + Time1: "2022-09-01", + Time2: "2022-09-01", + Time3: "2022-09-02", + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err.String()) + } + + // Output: + // The Time2 value `2022-09-01` must be after field Time1 value `2022-09-01` +} + +func Example_Rule_AfterEqual() { + type BizReq struct { + Time1 string + Time2 string `v:"after-equal:Time1"` + Time3 string `v:"after-equal:Time1"` + } + var ( + ctx = context.Background() + req = BizReq{ + Time1: "2022-09-02", + Time2: "2022-09-01", + Time3: "2022-09-02", + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Print(gstr.Join(err.Strings(), "\n")) + } + + // Output: + // The Time2 value `2022-09-01` must be after or equal to field Time1 value `2022-09-02` +} + +func Example_Rule_Before() { + type BizReq struct { + Time1 string `v:"before:Time3"` + Time2 string `v:"before:Time3"` + Time3 string + } + var ( + ctx = context.Background() + req = BizReq{ + Time1: "2022-09-02", + Time2: "2022-09-03", + Time3: "2022-09-03", + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err.String()) + } + + // Output: + // The Time2 value `2022-09-03` must be before field Time3 value `2022-09-03` +} + +func Example_Rule_BeforeEqual() { + type BizReq struct { + Time1 string `v:"before-equal:Time3"` + Time2 string `v:"before-equal:Time3"` + Time3 string + } + var ( + ctx = context.Background() + req = BizReq{ + Time1: "2022-09-02", + Time2: "2022-09-01", + Time3: "2022-09-01", + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Print(gstr.Join(err.Strings(), "\n")) + } + + // Output: + // The Time1 value `2022-09-02` must be before or equal to field Time3 +} + +func Example_Rule_Array() { + type BizReq struct { + Value1 string `v:"array"` + Value2 string `v:"array"` + Value3 string `v:"array"` + Value4 []string `v:"array"` + } + var ( + ctx = context.Background() + req = BizReq{ + Value1: "1,2,3", + Value2: "[]", + Value3: "[1,2,3]", + Value4: []string{}, + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Print(gstr.Join(err.Strings(), "\n")) + } + + // Output: + // The Value1 value `1,2,3` is not of valid array type +} + +func Example_Rule_EQ() { + type BizReq struct { + Name string `v:"required"` + Password string `v:"required|eq:Password2"` + Password2 string `v:"required"` + } + var ( + ctx = context.Background() + req = BizReq{ + Name: "gf", + Password: "goframe.org", + Password2: "goframe.net", + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err) + } + + // Output: + // The Password value `goframe.org` must be equal to field Password2 value `goframe.net` +} + +func Example_Rule_NotEQ() { + type BizReq struct { + Name string `v:"required"` + MailAddr string `v:"required"` + OtherMailAddr string `v:"required|not-eq:MailAddr"` + } + var ( + ctx = context.Background() + req = BizReq{ + Name: "gf", + MailAddr: "gf@goframe.org", + OtherMailAddr: "gf@goframe.org", + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err) + } + + // Output: + // The OtherMailAddr value `gf@goframe.org` must not be equal to field MailAddr value `gf@goframe.org` +} + +func Example_Rule_GT() { + type BizReq struct { + Value1 int + Value2 int `v:"gt:Value1"` + Value3 int `v:"gt:Value1"` + } + var ( + ctx = context.Background() + req = BizReq{ + Value1: 1, + Value2: 1, + Value3: 2, + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err.String()) + } + + // Output: + // The Value2 value `1` must be greater than field Value1 value `1` +} + +func Example_Rule_GTE() { + type BizReq struct { + Value1 int + Value2 int `v:"gte:Value1"` + Value3 int `v:"gte:Value1"` + } + var ( + ctx = context.Background() + req = BizReq{ + Value1: 2, + Value2: 1, + Value3: 2, + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err.String()) + } + + // Output: + // The Value2 value `1` must be greater than or equal to field Value1 value `2` +} + +func Example_Rule_LT() { + type BizReq struct { + Value1 int + Value2 int `v:"lt:Value1"` + Value3 int `v:"lt:Value1"` + } + var ( + ctx = context.Background() + req = BizReq{ + Value1: 2, + Value2: 1, + Value3: 2, + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err.String()) + } + + // Output: + // The Value3 value `2` must be lesser than field Value1 value `2` +} + +func Example_Rule_LTE() { + type BizReq struct { + Value1 int + Value2 int `v:"lte:Value1"` + Value3 int `v:"lte:Value1"` + } + var ( + ctx = context.Background() + req = BizReq{ + Value1: 1, + Value2: 1, + Value3: 2, + } + ) + if err := g.Validator().Data(req).Run(ctx); err != nil { + fmt.Println(err.String()) + } + + // Output: + // The Value3 value `2` must be lesser than or equal to field Value1 value `1` +} + +func Example_Rule_Foreach() { + type BizReq struct { + Value1 []int `v:"foreach|in:1,2,3"` + Value2 []int `v:"foreach|in:1,2,3"` + } + var ( + ctx = context.Background() + req = BizReq{ + Value1: []int{1, 2, 3}, + Value2: []int{3, 4, 5}, + } + ) + if err := g.Validator().Bail().Data(req).Run(ctx); err != nil { + fmt.Println(err.String()) + } + + // Output: + // The Value2 value `4` is not in acceptable range: 1,2,3 +} diff --git a/util/gvalid/gvalid_z_unit_feature_rule_test.go b/util/gvalid/gvalid_z_unit_feature_rule_test.go index 5229ef9b4..078b380bc 100755 --- a/util/gvalid/gvalid_z_unit_feature_rule_test.go +++ b/util/gvalid/gvalid_z_unit_feature_rule_test.go @@ -37,6 +37,33 @@ func Test_Check(t *testing.T) { }) } +func Test_Array(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data("1").Rules("array").Run(ctx) + t.Assert(err, "The value `1` is not of valid array type") + }) + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data("").Rules("array").Run(ctx) + t.Assert(err, "The value `` is not of valid array type") + }) + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data("[1,2,3]").Rules("array").Run(ctx) + t.Assert(err, "") + }) + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data("[]").Rules("array").Run(ctx) + t.Assert(err, "") + }) + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data([]int{1, 2, 3}).Rules("array").Run(ctx) + t.Assert(err, "") + }) + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data([]int{}).Rules("array").Run(ctx) + t.Assert(err, "") + }) +} + func Test_Required(t *testing.T) { if m := g.Validator().Data("1").Rules("required").Run(ctx); m != nil { t.Error(m) @@ -946,6 +973,52 @@ func Test_Different(t *testing.T) { }) } +func Test_EQ(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + rule := "eq:id" + val1 := "100" + params1 := g.Map{ + "age": 18, + } + params2 := g.Map{ + "id": 100, + } + params3 := g.Map{ + "id": 100, + "name": "john", + } + err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Run(ctx) + err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Run(ctx) + err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Run(ctx) + t.AssertNE(err1, nil) + t.Assert(err2, nil) + t.Assert(err3, nil) + }) +} + +func Test_Not_EQ(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + rule := "not-eq:id" + val1 := "100" + params1 := g.Map{ + "age": 18, + } + params2 := g.Map{ + "id": 100, + } + params3 := g.Map{ + "id": 100, + "name": "john", + } + err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Run(ctx) + err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Run(ctx) + err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Run(ctx) + t.Assert(err1, nil) + t.AssertNE(err2, nil) + t.AssertNE(err3, nil) + }) +} + func Test_In(t *testing.T) { gtest.C(t, func(t *gtest.T) { rule := "in:100,200" @@ -1025,6 +1098,18 @@ func Test_Regex2(t *testing.T) { }) } +func Test_Not_Regex(t *testing.T) { + rule := `not-regex:\d{6}|\D{6}|length:6,16` + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data("123456").Rules(rule).Run(ctx) + t.Assert(err, "The value `123456` should not be in regex of: \\d{6}|\\D{6}") + }) + gtest.C(t, func(t *gtest.T) { + err := g.Validator().Data("abcde6").Rules(rule).Run(ctx) + t.AssertNil(err) + }) +} + // issue: https://github.com/gogf/gf/issues/1077 func Test_InternalError_String(t *testing.T) { gtest.C(t, func(t *gtest.T) { @@ -1105,3 +1190,363 @@ func Test_Bail(t *testing.T) { t.Assert(err.Error(), "min number is 1") }) } + +func Test_After(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"after:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-02", + T2: "2022-09-01", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"after:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-01", + T2: "2022-09-02", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-01` must be after field T2 value `2022-09-02`") + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"after:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-02"), + T2: gtime.New("2022-09-01"), + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"after:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-01"), + T2: gtime.New("2022-09-02"), + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-01 00:00:00` must be after field T2 value `2022-09-02 00:00:00`") + }) +} + +func Test_After_Equal(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"after-equal:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-02", + T2: "2022-09-01", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"after-equal:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-01", + T2: "2022-09-02", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-01` must be after or equal to field T2 value `2022-09-02`") + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"after-equal:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-02"), + T2: gtime.New("2022-09-01"), + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"after-equal:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-01"), + T2: gtime.New("2022-09-01"), + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"after-equal:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-01"), + T2: gtime.New("2022-09-02"), + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-01 00:00:00` must be after or equal to field T2 value `2022-09-02 00:00:00`") + }) +} + +func Test_Before(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"before:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-01", + T2: "2022-09-02", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"before:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-02", + T2: "2022-09-01", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-02` must be before field T2 value `2022-09-01`") + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"before:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-01"), + T2: gtime.New("2022-09-02"), + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"before:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-02"), + T2: gtime.New("2022-09-01"), + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-02 00:00:00` must be before field T2 value `2022-09-01 00:00:00`") + }) +} + +func Test_Before_Equal(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"before-equal:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-01", + T2: "2022-09-02", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 string `v:"before-equal:T2"` + T2 string + } + obj := &Params{ + T1: "2022-09-02", + T2: "2022-09-01", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-02` must be before or equal to field T2") + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"before-equal:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-01"), + T2: gtime.New("2022-09-02"), + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"before-equal:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-01"), + T2: gtime.New("2022-09-01"), + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + T1 *gtime.Time `v:"before-equal:T2"` + T2 *gtime.Time + } + obj := &Params{ + T1: gtime.New("2022-09-02"), + T2: gtime.New("2022-09-01"), + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The T1 value `2022-09-02 00:00:00` must be before or equal to field T2") + }) +} + +func Test_GT(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"gt:V2"` + V2 string + } + obj := &Params{ + V1: "1.2", + V2: "1.1", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"gt:V2"` + V2 string + } + obj := &Params{ + V1: "1.1", + V2: "1.2", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The V1 value `1.1` must be greater than field V2 value `1.2`") + }) +} + +func Test_GTE(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"gte:V2"` + V2 string + } + obj := &Params{ + V1: "1.2", + V2: "1.1", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"gte:V2"` + V2 string + } + obj := &Params{ + V1: "1.1", + V2: "1.2", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The V1 value `1.1` must be greater than or equal to field V2 value `1.2`") + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"gte:V2"` + V2 string + } + obj := &Params{ + V1: "1.1", + V2: "1.1", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) +} + +func Test_LT(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"lt:V2"` + V2 string + } + obj := &Params{ + V1: "1.1", + V2: "1.2", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"lt:V2"` + V2 string + } + obj := &Params{ + V1: "1.2", + V2: "1.1", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The V1 value `1.2` must be lesser than field V2 value `1.1`") + }) +} + +func Test_LTE(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"lte:V2"` + V2 string + } + obj := &Params{ + V1: "1.1", + V2: "1.2", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"lte:V2"` + V2 string + } + obj := &Params{ + V1: "1.2", + V2: "1.1", + } + err := g.Validator().Data(obj).Run(ctx) + t.Assert(err, "The V1 value `1.2` must be lesser than or equal to field V2 value `1.1`") + }) + gtest.C(t, func(t *gtest.T) { + type Params struct { + V1 string `v:"lte:V2"` + V2 string + } + obj := &Params{ + V1: "1.1", + V2: "1.1", + } + err := g.Validator().Data(obj).Run(ctx) + t.AssertNil(err) + }) +} diff --git a/util/gvalid/i18n/cn/validation.toml b/util/gvalid/i18n/cn/validation.toml index 0601310b9..9c01b0dc3 100644 --- a/util/gvalid/i18n/cn/validation.toml +++ b/util/gvalid/i18n/cn/validation.toml @@ -1,47 +1,48 @@ -"gf.gvalid.rule.required" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-if" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-unless" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-with" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-with-all" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-without" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-without-all" = "{attribute}字段不能为空" -"gf.gvalid.rule.date" = "{attribute}字段值`{value}`日期格式不满足Y-m-d格式,例如: 2001-02-03" -"gf.gvalid.rule.datetime" = "{attribute}字段值`{value}`日期格式不满足Y-m-d H:i:s格式,例如: 2001-02-03 12:00:00" -"gf.gvalid.rule.date-format" = "{attribute}字段值`{value}`日期格式不满足{format}" -"gf.gvalid.rule.email" = "{attribute}字段值`{value}`邮箱地址格式不正确" -"gf.gvalid.rule.phone" = "{attribute}字段值`{value}`手机号码格式不正确" -"gf.gvalid.rule.phone-loose" = "{attribute}字段值`{value}`手机号码格式不正确" -"gf.gvalid.rule.telephone" = "{attribute}字段值`{value}`电话号码格式不正确" -"gf.gvalid.rule.passport" = "{attribute}字段值`{value}`账号格式不合法,必需以字母开头,只能包含字母、数字和下划线,长度在6~18之间" -"gf.gvalid.rule.password" = "{attribute}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符" -"gf.gvalid.rule.password2" = "{attribute}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母和数字" -"gf.gvalid.rule.password3" = "{attribute}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母、数字和特殊字符" -"gf.gvalid.rule.postcode" = "{attribute}字段值`{value}`邮政编码不正确" -"gf.gvalid.rule.resident-id" = "{attribute}字段值`{value}`身份证号码格式不正确" -"gf.gvalid.rule.bank-card" = "{attribute}字段值`{value}`银行卡号格式不正确" -"gf.gvalid.rule.qq" = "{attribute}字段值`{value}`QQ号码格式不正确" -"gf.gvalid.rule.ip" = "{attribute}字段值`{value}`IP地址格式不正确" -"gf.gvalid.rule.ipv4" = "{attribute}字段值`{value}`IPv4地址格式不正确" -"gf.gvalid.rule.ipv6" = "{attribute}字段值`{value}`IPv6地址格式不正确" -"gf.gvalid.rule.mac" = "{attribute}字段值`{value}`MAC地址格式不正确" -"gf.gvalid.rule.url" = "{attribute}字段值`{value}`URL地址格式不正确" -"gf.gvalid.rule.domain" = "{attribute}字段值`{value}`域名格式不正确" -"gf.gvalid.rule.length" = "{attribute}字段值`{value}`字段长度应当为{min}到{max}个字符" -"gf.gvalid.rule.min-length" = "{attribute}字段值`{value}`字段最小长度应当为{min}" -"gf.gvalid.rule.max-length" = "{attribute}字段值`{value}`字段最大长度应当为{max}" -"gf.gvalid.rule.size" = "{attribute}字段值`{value}`字段长度必须应当为{size}" -"gf.gvalid.rule.between" = "{attribute}字段值`{value}`字段大小应当为{min}到{max}" -"gf.gvalid.rule.min" = "{attribute}字段值`{value}`字段最小值应当为{min}" -"gf.gvalid.rule.max" = "{attribute}字段值`{value}`字段最大值应当为{max}" -"gf.gvalid.rule.json" = "{attribute}字段值`{value}`字段应当为JSON格式" -"gf.gvalid.rule.xml" = "{attribute}字段值`{value}`字段应当为XML格式" -"gf.gvalid.rule.array" = "{attribute}字段值`{value}`字段应当为数组" -"gf.gvalid.rule.integer" = "{attribute}字段值`{value}`字段应当为整数" -"gf.gvalid.rule.float" = "{attribute}字段值`{value}`字段应当为浮点数" -"gf.gvalid.rule.boolean" = "{attribute}字段值`{value}`字段应当为布尔值" -"gf.gvalid.rule.same" = "{attribute}字段值`{value}`字段值必须和{field}相同" -"gf.gvalid.rule.different" = "{attribute}字段值`{value}`字段值不能与{field}相同" -"gf.gvalid.rule.in" = "{attribute}字段值`{value}`字段值应当满足取值范围:{pattern}" -"gf.gvalid.rule.not-in" = "{attribute}字段值`{value}`字段值不应当满足取值范围:{pattern}" -"gf.gvalid.rule.regex" = "{attribute}字段值`{value}`字段值不满足规则:{pattern}" -"gf.gvalid.rule.__default__" = "{attribute}字段值`{value}`字段值不合法" \ No newline at end of file +# 本i18n文件仅供参考,没有功能作用。 +"gf.gvalid.rule.required" = "{field}字段不能为空" +"gf.gvalid.rule.required-if" = "{field}字段不能为空" +"gf.gvalid.rule.required-unless" = "{field}字段不能为空" +"gf.gvalid.rule.required-with" = "{field}字段不能为空" +"gf.gvalid.rule.required-with-all" = "{field}字段不能为空" +"gf.gvalid.rule.required-without" = "{field}字段不能为空" +"gf.gvalid.rule.required-without-all" = "{field}字段不能为空" +"gf.gvalid.rule.date" = "{field}字段值`{value}`日期格式不满足Y-m-d格式,例如: 2001-02-03" +"gf.gvalid.rule.datetime" = "{field}字段值`{value}`日期格式不满足Y-m-d H:i:s格式,例如: 2001-02-03 12:00:00" +"gf.gvalid.rule.date-format" = "{field}字段值`{value}`日期格式不满足{format}" +"gf.gvalid.rule.email" = "{field}字段值`{value}`邮箱地址格式不正确" +"gf.gvalid.rule.phone" = "{field}字段值`{value}`手机号码格式不正确" +"gf.gvalid.rule.phone-loose" = "{field}字段值`{value}`手机号码格式不正确" +"gf.gvalid.rule.telephone" = "{field}字段值`{value}`电话号码格式不正确" +"gf.gvalid.rule.passport" = "{field}字段值`{value}`账号格式不合法,必需以字母开头,只能包含字母、数字和下划线,长度在6~18之间" +"gf.gvalid.rule.password" = "{field}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符" +"gf.gvalid.rule.password2" = "{field}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母和数字" +"gf.gvalid.rule.password3" = "{field}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母、数字和特殊字符" +"gf.gvalid.rule.postcode" = "{field}字段值`{value}`邮政编码不正确" +"gf.gvalid.rule.resident-id" = "{field}字段值`{value}`身份证号码格式不正确" +"gf.gvalid.rule.bank-card" = "{field}字段值`{value}`银行卡号格式不正确" +"gf.gvalid.rule.qq" = "{field}字段值`{value}`QQ号码格式不正确" +"gf.gvalid.rule.ip" = "{field}字段值`{value}`IP地址格式不正确" +"gf.gvalid.rule.ipv4" = "{field}字段值`{value}`IPv4地址格式不正确" +"gf.gvalid.rule.ipv6" = "{field}字段值`{value}`IPv6地址格式不正确" +"gf.gvalid.rule.mac" = "{field}字段值`{value}`MAC地址格式不正确" +"gf.gvalid.rule.url" = "{field}字段值`{value}`URL地址格式不正确" +"gf.gvalid.rule.domain" = "{field}字段值`{value}`域名格式不正确" +"gf.gvalid.rule.length" = "{field}字段值`{value}`字段长度应当为{min}到{max}个字符" +"gf.gvalid.rule.min-length" = "{field}字段值`{value}`字段最小长度应当为{min}" +"gf.gvalid.rule.max-length" = "{field}字段值`{value}`字段最大长度应当为{max}" +"gf.gvalid.rule.size" = "{field}字段值`{value}`字段长度必须应当为{size}" +"gf.gvalid.rule.between" = "{field}字段值`{value}`字段大小应当为{min}到{max}" +"gf.gvalid.rule.min" = "{field}字段值`{value}`字段最小值应当为{min}" +"gf.gvalid.rule.max" = "{field}字段值`{value}`字段最大值应当为{max}" +"gf.gvalid.rule.json" = "{field}字段值`{value}`字段应当为JSON格式" +"gf.gvalid.rule.xml" = "{field}字段值`{value}`字段应当为XML格式" +"gf.gvalid.rule.array" = "{field}字段值`{value}`字段应当为数组" +"gf.gvalid.rule.integer" = "{field}字段值`{value}`字段应当为整数" +"gf.gvalid.rule.float" = "{field}字段值`{value}`字段应当为浮点数" +"gf.gvalid.rule.boolean" = "{field}字段值`{value}`字段应当为布尔值" +"gf.gvalid.rule.same" = "{field}字段值`{value}`字段值必须和{field}相同" +"gf.gvalid.rule.different" = "{field}字段值`{value}`字段值不能与{field}相同" +"gf.gvalid.rule.in" = "{field}字段值`{value}`字段值应当满足取值范围:{pattern}" +"gf.gvalid.rule.not-in" = "{field}字段值`{value}`字段值不应当满足取值范围:{pattern}" +"gf.gvalid.rule.regex" = "{field}字段值`{value}`字段值不满足规则:{pattern}" +"gf.gvalid.rule.__default__" = "{field}字段值`{value}`字段值不合法" \ No newline at end of file diff --git a/util/gvalid/i18n/en/validation.toml b/util/gvalid/i18n/en/validation.toml index 90f1a7f69..4bd849227 100644 --- a/util/gvalid/i18n/en/validation.toml +++ b/util/gvalid/i18n/en/validation.toml @@ -1,45 +1,45 @@ -"gf.gvalid.rule.required" = "The {attribute} field is required" -"gf.gvalid.rule.required-if" = "The {attribute} field is required" -"gf.gvalid.rule.required-unless" = "The {attribute} field is required" -"gf.gvalid.rule.required-with" = "The {attribute} field is required" -"gf.gvalid.rule.required-with-all" = "The {attribute} field is required" -"gf.gvalid.rule.required-without" = "The {attribute} field is required" -"gf.gvalid.rule.required-without-all" = "The {attribute} field is required" -"gf.gvalid.rule.date" = "The {attribute} value `{value}` is not a valid date" -"gf.gvalid.rule.datetime" = "The {attribute} value `{value}` is not a valid datetime" -"gf.gvalid.rule.date-format" = "The {attribute} value `{value}` does not match the format: {pattern}" -"gf.gvalid.rule.email" = "The {attribute} value `{value}` is not a valid email address" -"gf.gvalid.rule.phone" = "The {attribute} value `{value}` is not a valid phone number" -"gf.gvalid.rule.telephone" = "The {attribute} value `{value}` is not a valid telephone number" -"gf.gvalid.rule.passport" = "The {attribute} value `{value}` is not a valid passport format" -"gf.gvalid.rule.password" = "The {attribute} value `{value}` is not a valid password format" -"gf.gvalid.rule.password2" = "The {attribute} value `{value}` is not a valid password format" -"gf.gvalid.rule.password3" = "The {attribute} value `{value}` is not a valid password format" -"gf.gvalid.rule.postcode" = "The {attribute} value `{value}` is not a valid postcode format" -"gf.gvalid.rule.resident-id" = "The {attribute} value `{value}` is not a valid resident id number" -"gf.gvalid.rule.bank-card" = "The {attribute} value `{value}` is not a valid bank card number" -"gf.gvalid.rule.qq" = "The {attribute} value `{value}` is not a valid QQ number" -"gf.gvalid.rule.ip" = "The {attribute} value `{value}` is not a valid IP address" -"gf.gvalid.rule.ipv4" = "The {attribute} value `{value}` is not a valid IPv4 address" -"gf.gvalid.rule.ipv6" = "The {attribute} value `{value}` is not a valid IPv6 address" -"gf.gvalid.rule.mac" = "The {attribute} value `{value}` is not a valid MAC address" -"gf.gvalid.rule.url" = "The {attribute} value `{value}` is not a valid URL address" -"gf.gvalid.rule.domain" = "The {attribute} value `{value}` is not a valid domain format" -"gf.gvalid.rule.length" = "The {attribute} value `{value}` length must be between {min} and {max}" -"gf.gvalid.rule.min-length" = "The {attribute} value `{value}` length must be equal or greater than {min}" -"gf.gvalid.rule.max-length" = "The {attribute} value `{value}` length must be equal or lesser than {max}" -"gf.gvalid.rule.size" = "The {attribute} value `{value}` length must be {size}" -"gf.gvalid.rule.between" = "The {attribute} value `{value}` must be between {min} and {max}" -"gf.gvalid.rule.min" = "The {attribute} value `{value}` must be equal or greater than {min}" -"gf.gvalid.rule.max" = "The {attribute} value `{value}` must be equal or lesser than {max}" -"gf.gvalid.rule.json" = "The {attribute} value `{value}` is not a valid JSON string" -"gf.gvalid.rule.xml" = "The {attribute} value `{value}` is not a valid XML string" -"gf.gvalid.rule.array" = "The {attribute} value `{value}` is not an array" -"gf.gvalid.rule.integer" = "The {attribute} value `{value}` is not an integer" -"gf.gvalid.rule.boolean" = "The {attribute} value `{value}` field must be true or false" -"gf.gvalid.rule.same" = "The {attribute} value `{value}` must be the same as field {pattern}" -"gf.gvalid.rule.different" = "The {attribute} value `{value}` must be different from field {pattern}" -"gf.gvalid.rule.in" = "The {attribute} value `{value}` is not in acceptable range: {pattern}" -"gf.gvalid.rule.not-in" = "The {attribute} value `{value}` must not be in range: {pattern}" -"gf.gvalid.rule.regex" = "The {attribute} value `{value}` must be in regex of: {pattern}" +# This i18n file is just for reference, no functionanity purpose. +"gf.gvalid.rule.required" = "The {field} field is required" +"gf.gvalid.rule.required-if" = "The {field} field is required" +"gf.gvalid.rule.required-unless" = "The {field} field is required" +"gf.gvalid.rule.required-with" = "The {field} field is required" +"gf.gvalid.rule.required-with-all" = "The {field} field is required" +"gf.gvalid.rule.required-without" = "The {field} field is required" +"gf.gvalid.rule.required-without-all" = "The {field} field is required" +"gf.gvalid.rule.date" = "The {field} value `{value}` is not a valid date" +"gf.gvalid.rule.datetime" = "The {field} value `{value}` is not a valid datetime" +"gf.gvalid.rule.date-format" = "The {field} value `{value}` does not match the format: {pattern}" +"gf.gvalid.rule.email" = "The {field} value `{value}` is not a valid email address" +"gf.gvalid.rule.phone" = "The {field} value `{value}` is not a valid phone number" +"gf.gvalid.rule.telephone" = "The {field} value `{value}` is not a valid telephone number" +"gf.gvalid.rule.passport" = "The {field} value `{value}` is not a valid passport format" +"gf.gvalid.rule.password" = "The {field} value `{value}` is not a valid password format" +"gf.gvalid.rule.password2" = "The {field} value `{value}` is not a valid password format" +"gf.gvalid.rule.password3" = "The {field} value `{value}` is not a valid password format" +"gf.gvalid.rule.postcode" = "The {field} value `{value}` is not a valid postcode format" +"gf.gvalid.rule.resident-id" = "The {field} value `{value}` is not a valid resident id number" +"gf.gvalid.rule.bank-card" = "The {field} value `{value}` is not a valid bank card number" +"gf.gvalid.rule.qq" = "The {field} value `{value}` is not a valid QQ number" +"gf.gvalid.rule.ip" = "The {field} value `{value}` is not a valid IP address" +"gf.gvalid.rule.ipv4" = "The {field} value `{value}` is not a valid IPv4 address" +"gf.gvalid.rule.ipv6" = "The {field} value `{value}` is not a valid IPv6 address" +"gf.gvalid.rule.mac" = "The {field} value `{value}` is not a valid MAC address" +"gf.gvalid.rule.url" = "The {field} value `{value}` is not a valid URL address" +"gf.gvalid.rule.domain" = "The {field} value `{value}` is not a valid domain format" +"gf.gvalid.rule.length" = "The {field} value `{value}` length must be between {min} and {max}" +"gf.gvalid.rule.min-length" = "The {field} value `{value}` length must be equal or greater than {min}" +"gf.gvalid.rule.max-length" = "The {field} value `{value}` length must be equal or lesser than {max}" +"gf.gvalid.rule.size" = "The {field} value `{value}` length must be {size}" +"gf.gvalid.rule.between" = "The {field} value `{value}` must be between {min} and {max}" +"gf.gvalid.rule.min" = "The {field} value `{value}` must be equal or greater than {min}" +"gf.gvalid.rule.max" = "The {field} value `{value}` must be equal or lesser than {max}" +"gf.gvalid.rule.json" = "The {field} value `{value}` is not a valid JSON string" +"gf.gvalid.rule.xml" = "The {field} value `{value}` is not a valid XML string" +"gf.gvalid.rule.integer" = "The {field} value `{value}` is not an integer" +"gf.gvalid.rule.boolean" = "The {field} value `{value}` field must be true or false" +"gf.gvalid.rule.same" = "The {field} value `{value}` must be the same as field {pattern}" +"gf.gvalid.rule.different" = "The {field} value `{value}` must be different from field {pattern}" +"gf.gvalid.rule.in" = "The {field} value `{value}` is not in acceptable range: {pattern}" +"gf.gvalid.rule.not-in" = "The {field} value `{value}` must not be in range: {pattern}" +"gf.gvalid.rule.regex" = "The {field} value `{value}` must be in regex of: {pattern}" "gf.gvalid.rule.gf.gvalid.rule.__default__" = "The :attribute value `:value` is invalid" \ No newline at end of file diff --git a/util/gvalid/internal/builtin/builtin.go b/util/gvalid/internal/builtin/builtin.go new file mode 100644 index 000000000..71fa3a64c --- /dev/null +++ b/util/gvalid/internal/builtin/builtin.go @@ -0,0 +1,55 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package builtin implements built-in validation rules. +// +// Referred to Laravel validation: +// https://laravel.com/docs/master/validation#available-validation-rules +package builtin + +import ( + "github.com/gogf/gf/v2/container/gvar" +) + +type Rule interface { + // Name returns the builtin name of the rule. + Name() string + + // Message returns the default error message of the rule. + Message() string + + // Run starts running the rule, it returns nil if successful, or else an error. + Run(in RunInput) error +} + +type RunInput struct { + RuleKey string // RuleKey is like the "max" in rule "max: 6" + RulePattern string // RulePattern is like "6" in rule:"max:6" + Field string // The field name of Value. + Value *gvar.Var // Value specifies the value for this rule to validate. + Data *gvar.Var // Data specifies the `data` which is passed to the Validator. + Message string // Message specifies the custom error message or configured i18n message for this rule. + Option RunOption // Option provides extra configuration for validation rule. +} + +type RunOption struct { + CaseInsensitive bool // CaseInsensitive indicates that it does Case-Insensitive comparison in string. +} + +var ( + // ruleMap stores all builtin validation rules. + ruleMap = map[string]Rule{} +) + +// Register registers builtin rule into manager. +func Register(rule Rule) { + ruleMap[rule.Name()] = rule +} + +// GetRule retrieves and returns rule by `name`. +func GetRule(name string) Rule { + return ruleMap[name] +} diff --git a/util/gvalid/internal/builtin/builtin_after.go b/util/gvalid/internal/builtin/builtin_after.go new file mode 100644 index 000000000..cf4312632 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_after.go @@ -0,0 +1,48 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleAfter implements `after` rule: +// The datetime value should be after the value of field `field`. +// +// Format: after:field +type RuleAfter struct{} + +func init() { + Register(RuleAfter{}) +} + +func (r RuleAfter) Name() string { + return "after" +} + +func (r RuleAfter) Message() string { + return "The {field} value `{value}` must be after field {field1} value `{value1}`" +} + +func (r RuleAfter) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + valueDatetime = in.Value.Time() + fieldDatetime = gconv.Time(fieldValue) + ) + if valueDatetime.After(fieldDatetime) { + return nil + } + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) +} diff --git a/util/gvalid/internal/builtin/builtin_after_equal.go b/util/gvalid/internal/builtin/builtin_after_equal.go new file mode 100644 index 000000000..569ac70ce --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_after_equal.go @@ -0,0 +1,48 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleAfterEqual implements `after-equal` rule: +// The datetime value should be after or equal to the value of field `field`. +// +// Format: after-equal:field +type RuleAfterEqual struct{} + +func init() { + Register(RuleAfterEqual{}) +} + +func (r RuleAfterEqual) Name() string { + return "after-equal" +} + +func (r RuleAfterEqual) Message() string { + return "The {field} value `{value}` must be after or equal to field {field1} value `{value1}`" +} + +func (r RuleAfterEqual) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + valueDatetime = in.Value.Time() + fieldDatetime = gconv.Time(fieldValue) + ) + if valueDatetime.After(fieldDatetime) || valueDatetime.Equal(fieldDatetime) { + return nil + } + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) +} diff --git a/util/gvalid/internal/builtin/builtin_array.go b/util/gvalid/internal/builtin/builtin_array.go new file mode 100644 index 000000000..b051e0a81 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_array.go @@ -0,0 +1,44 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/internal/json" +) + +// RuleArray implements `array` rule: +// Value should be type of array. +// +// Format: array +type RuleArray struct{} + +func init() { + Register(RuleArray{}) +} + +func (r RuleArray) Name() string { + return "array" +} + +func (r RuleArray) Message() string { + return "The {field} value `{value}` is not of valid array type" +} + +func (r RuleArray) Run(in RunInput) error { + if in.Value.IsSlice() { + return nil + } + if json.Valid(in.Value.Bytes()) { + value := in.Value.String() + if len(value) > 1 && value[0] == '[' && value[len(value)-1] == ']' { + return nil + } + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_bail.go b/util/gvalid/internal/builtin/builtin_bail.go new file mode 100644 index 000000000..c848f7837 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_bail.go @@ -0,0 +1,29 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +// RuleBail implements `bail` rule: +// Stop validating when this field's validation failed. +// +// Format: bail +type RuleBail struct{} + +func init() { + Register(RuleBail{}) +} + +func (r RuleBail) Name() string { + return "bail" +} + +func (r RuleBail) Message() string { + return "" +} + +func (r RuleBail) Run(in RunInput) error { + return nil +} diff --git a/util/gvalid/gvalid_validator_rule_luhn.go b/util/gvalid/internal/builtin/builtin_bank_card.go similarity index 52% rename from util/gvalid/gvalid_validator_rule_luhn.go rename to util/gvalid/internal/builtin/builtin_bank_card.go index 9d743535d..efa86f5ab 100644 --- a/util/gvalid/gvalid_validator_rule_luhn.go +++ b/util/gvalid/internal/builtin/builtin_bank_card.go @@ -4,11 +4,40 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package gvalid +package builtin + +import ( + "errors" +) + +// RuleBankCard implements `bank-card` rule: +// Bank card number. +// +// Format: bank-card +type RuleBankCard struct{} + +func init() { + Register(RuleBankCard{}) +} + +func (r RuleBankCard) Name() string { + return "bank-card" +} + +func (r RuleBankCard) Message() string { + return "The {field} value `{value}` is not a valid bank card number" +} + +func (r RuleBankCard) Run(in RunInput) error { + if r.checkLuHn(in.Value.String()) { + return nil + } + return errors.New(in.Message) +} // checkLuHn checks `value` with LUHN algorithm. // It's usually used for bank card number validation. -func (v *Validator) checkLuHn(value string) bool { +func (r RuleBankCard) checkLuHn(value string) bool { var ( sum = 0 nDigits = len(value) diff --git a/util/gvalid/internal/builtin/builtin_before.go b/util/gvalid/internal/builtin/builtin_before.go new file mode 100644 index 000000000..cad27fd02 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_before.go @@ -0,0 +1,48 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleBefore implements `before` rule: +// The datetime value should be after the value of field `field`. +// +// Format: before:field +type RuleBefore struct{} + +func init() { + Register(RuleBefore{}) +} + +func (r RuleBefore) Name() string { + return "before" +} + +func (r RuleBefore) Message() string { + return "The {field} value `{value}` must be before field {field1} value `{value1}`" +} + +func (r RuleBefore) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + valueDatetime = in.Value.Time() + fieldDatetime = gconv.Time(fieldValue) + ) + if valueDatetime.Before(fieldDatetime) { + return nil + } + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) +} diff --git a/util/gvalid/internal/builtin/builtin_before_equal.go b/util/gvalid/internal/builtin/builtin_before_equal.go new file mode 100644 index 000000000..562408b47 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_before_equal.go @@ -0,0 +1,48 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleBeforeEqual implements `before-equal` rule: +// The datetime value should be after or equal to the value of field `field`. +// +// Format: before-equal:field +type RuleBeforeEqual struct{} + +func init() { + Register(RuleBeforeEqual{}) +} + +func (r RuleBeforeEqual) Name() string { + return "before-equal" +} + +func (r RuleBeforeEqual) Message() string { + return "The {field} value `{value}` must be before or equal to field {pattern}" +} + +func (r RuleBeforeEqual) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + valueDatetime = in.Value.Time() + fieldDatetime = gconv.Time(fieldValue) + ) + if valueDatetime.Before(fieldDatetime) || valueDatetime.Equal(fieldDatetime) { + return nil + } + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) +} diff --git a/util/gvalid/internal/builtin/builtin_between.go b/util/gvalid/internal/builtin/builtin_between.go new file mode 100644 index 000000000..ebfcdbf64 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_between.go @@ -0,0 +1,59 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + "strings" + + "github.com/gogf/gf/v2/text/gstr" +) + +// RuleBetween implements `between` rule: +// Range between :min and :max. It supports both integer and float. +// +// Format: between:min,max +type RuleBetween struct{} + +func init() { + Register(RuleBetween{}) +} + +func (r RuleBetween) Name() string { + return "between" +} + +func (r RuleBetween) Message() string { + return "The {field} value `{value}` must be between {min} and {max}" +} + +func (r RuleBetween) Run(in RunInput) error { + var ( + array = strings.Split(in.RulePattern, ",") + min = float64(0) + max = float64(0) + ) + if len(array) > 0 { + if v, err := strconv.ParseFloat(strings.TrimSpace(array[0]), 10); err == nil { + min = v + } + } + if len(array) > 1 { + if v, err := strconv.ParseFloat(strings.TrimSpace(array[1]), 10); err == nil { + max = v + } + } + valueF, err := strconv.ParseFloat(in.Value.String(), 10) + if valueF < min || valueF > max || err != nil { + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{min}": strconv.FormatFloat(min, 'f', -1, 64), + "{max}": strconv.FormatFloat(max, 'f', -1, 64), + })) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_boolean.go b/util/gvalid/internal/builtin/builtin_boolean.go new file mode 100644 index 000000000..33465625f --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_boolean.go @@ -0,0 +1,50 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" +) + +// RuleBoolean implements `boolean` rule: +// Boolean(1,true,on,yes:true | 0,false,off,no,"":false) +// +// Format: boolean +type RuleBoolean struct{} + +// boolMap defines the boolean values. +var boolMap = map[string]struct{}{ + "1": {}, + "true": {}, + "on": {}, + "yes": {}, + "": {}, + "0": {}, + "false": {}, + "off": {}, + "no": {}, +} + +func init() { + Register(RuleBoolean{}) +} + +func (r RuleBoolean) Name() string { + return "boolean" +} + +func (r RuleBoolean) Message() string { + return "The {field} value `{value}` field must be true or false" +} + +func (r RuleBoolean) Run(in RunInput) error { + if _, ok := boolMap[strings.ToLower(in.Value.String())]; ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_ci.go b/util/gvalid/internal/builtin/builtin_ci.go new file mode 100644 index 000000000..8a1bf1198 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_ci.go @@ -0,0 +1,30 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +// RuleCi implements `ci` rule: +// Case-Insensitive configuration for those rules that need value comparison like: +// same, different, in, not-in, etc. +// +// Format: ci +type RuleCi struct{} + +func init() { + Register(RuleCi{}) +} + +func (r RuleCi) Name() string { + return "ci" +} + +func (r RuleCi) Message() string { + return "" +} + +func (r RuleCi) Run(in RunInput) error { + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_date.go b/util/gvalid/internal/builtin/builtin_date.go new file mode 100644 index 000000000..d186bbd64 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_date.go @@ -0,0 +1,52 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "time" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleDate implements `date` rule: +// Standard date, like: 2006-01-02, 20060102, 2006.01.02. +// +// Format: date +type RuleDate struct{} + +func init() { + Register(RuleDate{}) +} + +func (r RuleDate) Name() string { + return "date" +} + +func (r RuleDate) Message() string { + return "The {field} value `{value}` is not a valid date" +} + +func (r RuleDate) Run(in RunInput) error { + type iTime interface { + Date() (year int, month time.Month, day int) + IsZero() bool + } + // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. + if obj, ok := in.Value.Val().(iTime); ok { + if obj.IsZero() { + return errors.New(in.Message) + } + } + if !gregex.IsMatchString( + `\d{4}[\.\-\_/]{0,1}\d{2}[\.\-\_/]{0,1}\d{2}`, + in.Value.String(), + ) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_date_format.go b/util/gvalid/internal/builtin/builtin_date_format.go new file mode 100644 index 000000000..cfd6a8f59 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_date_format.go @@ -0,0 +1,50 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "time" + + "github.com/gogf/gf/v2/os/gtime" +) + +// RuleDateFormat implements `date-format` rule: +// Custom date format. +// +// Format: date-format:format +type RuleDateFormat struct{} + +func init() { + Register(RuleDateFormat{}) +} + +func (r RuleDateFormat) Name() string { + return "date-format" +} + +func (r RuleDateFormat) Message() string { + return "The {field} value `{value}` does not match the format: {pattern}" +} + +func (r RuleDateFormat) Run(in RunInput) error { + type iTime interface { + Date() (year int, month time.Month, day int) + IsZero() bool + } + // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. + if obj, ok := in.Value.Val().(iTime); ok { + if obj.IsZero() { + return errors.New(in.Message) + } + return nil + } + if _, err := gtime.StrToTimeFormat(in.Value.String(), in.RulePattern); err != nil { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_datetime.go b/util/gvalid/internal/builtin/builtin_datetime.go new file mode 100644 index 000000000..82b60e46e --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_datetime.go @@ -0,0 +1,50 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "time" + + "github.com/gogf/gf/v2/os/gtime" +) + +// RuleDatetime implements `datetime` rule: +// Standard datetime, like: 2006-01-02 12:00:00. +// +// Format: datetime +type RuleDatetime struct{} + +func init() { + Register(RuleDatetime{}) +} + +func (r RuleDatetime) Name() string { + return "datetime" +} + +func (r RuleDatetime) Message() string { + return "The {field} value `{value}` is not a valid datetime" +} + +func (r RuleDatetime) Run(in RunInput) error { + type iTime interface { + Date() (year int, month time.Month, day int) + IsZero() bool + } + // support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. + if obj, ok := in.Value.Val().(iTime); ok { + if obj.IsZero() { + return errors.New(in.Message) + } + return nil + } + if _, err := gtime.StrToTimeFormat(in.Value.String(), `Y-m-d H:i:s`); err != nil { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_different.go b/util/gvalid/internal/builtin/builtin_different.go new file mode 100644 index 000000000..d028aef3e --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_different.go @@ -0,0 +1,56 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleDifferent implements `different` rule: +// Value should be different from value of field. +// +// Format: different:field +type RuleDifferent struct{} + +func init() { + Register(RuleDifferent{}) +} + +func (r RuleDifferent) Name() string { + return "different" +} + +func (r RuleDifferent) Message() string { + return "The {field} value `{value}` must be different from field {field1} value `{value1}`" +} + +func (r RuleDifferent) Run(in RunInput) error { + var ( + ok = true + value = in.Value.String() + ) + fieldName, fieldValue := gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + if fieldValue != nil { + if in.Option.CaseInsensitive { + ok = !strings.EqualFold(value, gconv.String(fieldValue)) + } else { + ok = strings.Compare(value, gconv.String(fieldValue)) != 0 + } + } + if ok { + return nil + } + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) +} diff --git a/util/gvalid/internal/builtin/builtin_domain.go b/util/gvalid/internal/builtin/builtin_domain.go new file mode 100644 index 000000000..adca4472c --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_domain.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleDomain implements `domain` rule: +// Domain. +// +// Format: domain +type RuleDomain struct{} + +func init() { + Register(RuleDomain{}) +} + +func (r RuleDomain) Name() string { + return "domain" +} + +func (r RuleDomain) Message() string { + return "The {field} value `{value}` is not a valid domain format" +} + +func (r RuleDomain) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^([0-9a-zA-Z][0-9a-zA-Z\-]{0,62}\.)+([a-zA-Z]{0,62})$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_email.go b/util/gvalid/internal/builtin/builtin_email.go new file mode 100644 index 000000000..e3ea0ddc3 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_email.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleEmail implements `email` rule: +// Email address. +// +// Format: email +type RuleEmail struct{} + +func init() { + Register(RuleEmail{}) +} + +func (r RuleEmail) Name() string { + return "email" +} + +func (r RuleEmail) Message() string { + return "The {field} value `{value}` is not a valid email address" +} + +func (r RuleEmail) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)+$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_eq.go b/util/gvalid/internal/builtin/builtin_eq.go new file mode 100644 index 000000000..1aca7ec00 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_eq.go @@ -0,0 +1,31 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +// RuleEq implements `eq` rule: +// Value should be the same as value of field. +// +// This rule performs the same as rule `same`. +// +// Format: eq:field +type RuleEq struct{} + +func init() { + Register(RuleEq{}) +} + +func (r RuleEq) Name() string { + return "eq" +} + +func (r RuleEq) Message() string { + return "The {field} value `{value}` must be equal to field {field1} value `{value1}`" +} + +func (r RuleEq) Run(in RunInput) error { + return RuleSame{}.Run(in) +} diff --git a/util/gvalid/internal/builtin/builtin_float.go b/util/gvalid/internal/builtin/builtin_float.go new file mode 100644 index 000000000..cbf93d5b6 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_float.go @@ -0,0 +1,37 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" +) + +// RuleFloat implements `float` rule: +// Float. Note that an integer is actually a float number. +// +// Format: float +type RuleFloat struct{} + +func init() { + Register(RuleFloat{}) +} + +func (r RuleFloat) Name() string { + return "float" +} + +func (r RuleFloat) Message() string { + return "The {field} value `{value}` is not of valid float type" +} + +func (r RuleFloat) Run(in RunInput) error { + if _, err := strconv.ParseFloat(in.Value.String(), 10); err == nil { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_foreach.go b/util/gvalid/internal/builtin/builtin_foreach.go new file mode 100644 index 000000000..f169f2686 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_foreach.go @@ -0,0 +1,29 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +// RuleForeach implements `foreach` rule: +// It tells the next validation using current value as an array and validates each of its element. +// +// Format: foreach +type RuleForeach struct{} + +func init() { + Register(RuleForeach{}) +} + +func (r RuleForeach) Name() string { + return "foreach" +} + +func (r RuleForeach) Message() string { + return "" +} + +func (r RuleForeach) Run(in RunInput) error { + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_gt.go b/util/gvalid/internal/builtin/builtin_gt.go new file mode 100644 index 000000000..369fff2a0 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_gt.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleGT implements `gt` rule: +// Greater than `field`. +// It supports both integer and float. +// +// Format: gt:field +type RuleGT struct{} + +func init() { + Register(RuleGT{}) +} + +func (r RuleGT) Name() string { + return "gt" +} + +func (r RuleGT) Message() string { + return "The {field} value `{value}` must be greater than field {field1} value `{value1}`" +} + +func (r RuleGT) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + fieldValueN, err1 = strconv.ParseFloat(gconv.String(fieldValue), 10) + valueN, err2 = strconv.ParseFloat(in.Value.String(), 10) + ) + + if valueN <= fieldValueN || err1 != nil || err2 != nil { + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_gte.go b/util/gvalid/internal/builtin/builtin_gte.go new file mode 100644 index 000000000..e25a97869 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_gte.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleGTE implements `gte` rule: +// Greater than or equal to `field`. +// It supports both integer and float. +// +// Format: gte:field +type RuleGTE struct{} + +func init() { + Register(RuleGTE{}) +} + +func (r RuleGTE) Name() string { + return "gte" +} + +func (r RuleGTE) Message() string { + return "The {field} value `{value}` must be greater than or equal to field {field1} value `{value1}`" +} + +func (r RuleGTE) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + fieldValueN, err1 = strconv.ParseFloat(gconv.String(fieldValue), 10) + valueN, err2 = strconv.ParseFloat(in.Value.String(), 10) + ) + + if valueN < fieldValueN || err1 != nil || err2 != nil { + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_in.go b/util/gvalid/internal/builtin/builtin_in.go new file mode 100644 index 000000000..838b64d47 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_in.go @@ -0,0 +1,47 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/text/gstr" +) + +// RuleIn implements `in` rule: +// Value should be in: value1,value2,... +// +// Format: in:value1,value2,... +type RuleIn struct{} + +func init() { + Register(RuleIn{}) +} + +func (r RuleIn) Name() string { + return "in" +} + +func (r RuleIn) Message() string { + return "The {field} value `{value}` is not in acceptable range: {pattern}" +} + +func (r RuleIn) Run(in RunInput) error { + var ok bool + for _, rulePattern := range gstr.SplitAndTrim(in.RulePattern, ",") { + if in.Option.CaseInsensitive { + ok = strings.EqualFold(in.Value.String(), strings.TrimSpace(rulePattern)) + } else { + ok = strings.Compare(in.Value.String(), strings.TrimSpace(rulePattern)) == 0 + } + if ok { + return nil + } + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_integer.go b/util/gvalid/internal/builtin/builtin_integer.go new file mode 100644 index 000000000..228021435 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_integer.go @@ -0,0 +1,37 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" +) + +// RuleInteger implements `integer` rule: +// Integer. +// +// Format: integer +type RuleInteger struct{} + +func init() { + Register(RuleInteger{}) +} + +func (r RuleInteger) Name() string { + return "integer" +} + +func (r RuleInteger) Message() string { + return "The {field} value `{value}` is not an integer" +} + +func (r RuleInteger) Run(in RunInput) error { + if _, err := strconv.Atoi(in.Value.String()); err == nil { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_ip.go b/util/gvalid/internal/builtin/builtin_ip.go new file mode 100644 index 000000000..cf5ed4090 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_ip.go @@ -0,0 +1,43 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/net/gipv4" + "github.com/gogf/gf/v2/net/gipv6" +) + +// RuleIp implements `ip` rule: +// IPv4/IPv6. +// +// Format: ip +type RuleIp struct{} + +func init() { + Register(RuleIp{}) +} + +func (r RuleIp) Name() string { + return "ip" +} + +func (r RuleIp) Message() string { + return "The {field} value `{value}` is not a valid IP address" +} + +func (r RuleIp) Run(in RunInput) error { + var ( + ok bool + value = in.Value.String() + ) + if ok = gipv4.Validate(value) || gipv6.Validate(value); !ok { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_ipv4.go b/util/gvalid/internal/builtin/builtin_ipv4.go new file mode 100644 index 000000000..f37770e71 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_ipv4.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/net/gipv4" +) + +// RuleIpv4 implements `ipv4` rule: +// IPv4. +// +// Format: ipv4 +type RuleIpv4 struct{} + +func init() { + Register(RuleIpv4{}) +} + +func (r RuleIpv4) Name() string { + return "ipv4" +} + +func (r RuleIpv4) Message() string { + return "The {field} value `{value}` is not a valid IPv4 address" +} + +func (r RuleIpv4) Run(in RunInput) error { + var ( + ok bool + value = in.Value.String() + ) + if ok = gipv4.Validate(value); !ok { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_ipv6.go b/util/gvalid/internal/builtin/builtin_ipv6.go new file mode 100644 index 000000000..fa9cdfea1 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_ipv6.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/net/gipv6" +) + +// RuleIpv6 implements `ipv6` rule: +// IPv6. +// +// Format: ipv6 +type RuleIpv6 struct{} + +func init() { + Register(RuleIpv6{}) +} + +func (r RuleIpv6) Name() string { + return "ipv6" +} + +func (r RuleIpv6) Message() string { + return "The {field} value `{value}` is not a valid IPv6 address" +} + +func (r RuleIpv6) Run(in RunInput) error { + var ( + ok bool + value = in.Value.String() + ) + if ok = gipv6.Validate(value); !ok { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_json.go b/util/gvalid/internal/builtin/builtin_json.go new file mode 100644 index 000000000..0f1be7868 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_json.go @@ -0,0 +1,38 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/internal/json" +) + +// RuleJson implements `json` rule: +// JSON. +// +// Format: json +type RuleJson struct{} + +func init() { + Register(RuleJson{}) +} + +func (r RuleJson) Name() string { + return "json" +} + +func (r RuleJson) Message() string { + return "The {field} value `{value}` is not a valid JSON string" +} + +func (r RuleJson) Run(in RunInput) error { + if json.Valid(in.Value.Bytes()) { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_length.go b/util/gvalid/internal/builtin/builtin_length.go new file mode 100644 index 000000000..c99de47df --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_length.go @@ -0,0 +1,64 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + "strings" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" +) + +// RuleLength implements `length` rule: +// Length between :min and :max. +// The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. +// +// Format: length:min,max +type RuleLength struct{} + +func init() { + Register(RuleLength{}) +} + +func (r RuleLength) Name() string { + return "length" +} + +func (r RuleLength) Message() string { + return "The {field} value `{value}` length must be between {min} and {max}" +} + +func (r RuleLength) Run(in RunInput) error { + var ( + valueRunes = gconv.Runes(in.Value.String()) + valueLen = len(valueRunes) + ) + var ( + min = 0 + max = 0 + array = strings.Split(in.RulePattern, ",") + ) + if len(array) > 0 { + if v, err := strconv.Atoi(strings.TrimSpace(array[0])); err == nil { + min = v + } + } + if len(array) > 1 { + if v, err := strconv.Atoi(strings.TrimSpace(array[1])); err == nil { + max = v + } + } + if valueLen < min || valueLen > max { + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{min}": strconv.Itoa(min), + "{max}": strconv.Itoa(max), + })) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_lt.go b/util/gvalid/internal/builtin/builtin_lt.go new file mode 100644 index 000000000..f5ada9245 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_lt.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleLT implements `lt` rule: +// Lesser than `field`. +// It supports both integer and float. +// +// Format: lt:field +type RuleLT struct{} + +func init() { + Register(RuleLT{}) +} + +func (r RuleLT) Name() string { + return "lt" +} + +func (r RuleLT) Message() string { + return "The {field} value `{value}` must be lesser than field {field1} value `{value1}`" +} + +func (r RuleLT) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + fieldValueN, err1 = strconv.ParseFloat(gconv.String(fieldValue), 10) + valueN, err2 = strconv.ParseFloat(in.Value.String(), 10) + ) + + if valueN >= fieldValueN || err1 != nil || err2 != nil { + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_lte.go b/util/gvalid/internal/builtin/builtin_lte.go new file mode 100644 index 000000000..c5511221e --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_lte.go @@ -0,0 +1,51 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleLTE implements `lte` rule: +// Lesser than or equal to `field`. +// It supports both integer and float. +// +// Format: lte:field +type RuleLTE struct{} + +func init() { + Register(RuleLTE{}) +} + +func (r RuleLTE) Name() string { + return "lte" +} + +func (r RuleLTE) Message() string { + return "The {field} value `{value}` must be lesser than or equal to field {field1} value `{value1}`" +} + +func (r RuleLTE) Run(in RunInput) error { + var ( + fieldName, fieldValue = gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + fieldValueN, err1 = strconv.ParseFloat(gconv.String(fieldValue), 10) + valueN, err2 = strconv.ParseFloat(in.Value.String(), 10) + ) + + if valueN > fieldValueN || err1 != nil || err2 != nil { + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_mac.go b/util/gvalid/internal/builtin/builtin_mac.go new file mode 100644 index 000000000..d09f41367 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_mac.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleMac implements `mac` rule: +// MAC. +// +// Format: mac +type RuleMac struct{} + +func init() { + Register(RuleMac{}) +} + +func (r RuleMac) Name() string { + return "mac" +} + +func (r RuleMac) Message() string { + return "The {field} value `{value}` is not a valid MAC address" +} + +func (r RuleMac) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^([0-9A-Fa-f]{2}[\-:]){5}[0-9A-Fa-f]{2}$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_max.go b/util/gvalid/internal/builtin/builtin_max.go new file mode 100644 index 000000000..6dcb3dc33 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_max.go @@ -0,0 +1,43 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" +) + +// RuleMax implements `max` rule: +// Equal or lesser than :max. It supports both integer and float. +// +// Format: max:max +type RuleMax struct{} + +func init() { + Register(RuleMax{}) +} + +func (r RuleMax) Name() string { + return "max" +} + +func (r RuleMax) Message() string { + return "The {field} value `{value}` must be equal or lesser than {max}" +} + +func (r RuleMax) Run(in RunInput) error { + var ( + max, err1 = strconv.ParseFloat(in.RulePattern, 10) + valueN, err2 = strconv.ParseFloat(in.Value.String(), 10) + ) + if valueN > max || err1 != nil || err2 != nil { + return errors.New(gstr.Replace(in.Message, "{max}", strconv.FormatFloat(max, 'f', -1, 64))) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_max_length.go b/util/gvalid/internal/builtin/builtin_max_length.go new file mode 100644 index 000000000..e9847c973 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_max_length.go @@ -0,0 +1,46 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" +) + +// RuleMaxLength implements `max-length` rule: +// Length is equal or lesser than :max. +// The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. +// +// Format: max-length:max +type RuleMaxLength struct{} + +func init() { + Register(RuleMaxLength{}) +} + +func (r RuleMaxLength) Name() string { + return "max-length" +} + +func (r RuleMaxLength) Message() string { + return "The {field} value `{value}` length must be equal or lesser than {max}" +} + +func (r RuleMaxLength) Run(in RunInput) error { + var ( + valueRunes = gconv.Runes(in.Value.String()) + valueLen = len(valueRunes) + ) + max, err := strconv.Atoi(in.RulePattern) + if valueLen > max || err != nil { + return errors.New(gstr.Replace(in.Message, "{max}", strconv.Itoa(max))) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_min.go b/util/gvalid/internal/builtin/builtin_min.go new file mode 100644 index 000000000..5ee370a7f --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_min.go @@ -0,0 +1,43 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" +) + +// RuleMin implements `min` rule: +// Equal or greater than :min. It supports both integer and float. +// +// Format: min:min +type RuleMin struct{} + +func init() { + Register(RuleMin{}) +} + +func (r RuleMin) Name() string { + return "min" +} + +func (r RuleMin) Message() string { + return "The {field} value `{value}` must be equal or greater than {min}" +} + +func (r RuleMin) Run(in RunInput) error { + var ( + min, err1 = strconv.ParseFloat(in.RulePattern, 10) + valueN, err2 = strconv.ParseFloat(in.Value.String(), 10) + ) + if valueN < min || err1 != nil || err2 != nil { + return errors.New(gstr.Replace(in.Message, "{min}", strconv.FormatFloat(min, 'f', -1, 64))) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_min_length.go b/util/gvalid/internal/builtin/builtin_min_length.go new file mode 100644 index 000000000..cf5c1e2cd --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_min_length.go @@ -0,0 +1,46 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" +) + +// RuleMinLength implements `min-length` rule: +// Length is equal or greater than :min. +// The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. +// +// Format: min-length:min +type RuleMinLength struct{} + +func init() { + Register(RuleMinLength{}) +} + +func (r RuleMinLength) Name() string { + return "min-length" +} + +func (r RuleMinLength) Message() string { + return "The {field} value `{value}` length must be equal or greater than {min}" +} + +func (r RuleMinLength) Run(in RunInput) error { + var ( + valueRunes = gconv.Runes(in.Value.String()) + valueLen = len(valueRunes) + ) + min, err := strconv.Atoi(in.RulePattern) + if valueLen < min || err != nil { + return errors.New(gstr.Replace(in.Message, "{min}", strconv.Itoa(min))) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_not_eq.go b/util/gvalid/internal/builtin/builtin_not_eq.go new file mode 100644 index 000000000..5c502137b --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_not_eq.go @@ -0,0 +1,29 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +// RuleNotEq implements `not-eq` rule: +// Value should be different from value of field. +// +// Format: not-eq:field +type RuleNotEq struct{} + +func init() { + Register(RuleNotEq{}) +} + +func (r RuleNotEq) Name() string { + return "not-eq" +} + +func (r RuleNotEq) Message() string { + return "The {field} value `{value}` must not be equal to field {field1} value `{value1}`" +} + +func (r RuleNotEq) Run(in RunInput) error { + return RuleDifferent{}.Run(in) +} diff --git a/util/gvalid/internal/builtin/builtin_not_in.go b/util/gvalid/internal/builtin/builtin_not_in.go new file mode 100644 index 000000000..90fd79153 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_not_in.go @@ -0,0 +1,50 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/text/gstr" +) + +// RuleNotIn implements `not-in` rule: +// Value should not be in: value1,value2,... +// +// Format: not-in:value1,value2,... +type RuleNotIn struct{} + +func init() { + Register(RuleNotIn{}) +} + +func (r RuleNotIn) Name() string { + return "not-in" +} + +func (r RuleNotIn) Message() string { + return "The {field} value `{value}` must not be in range: {pattern}" +} + +func (r RuleNotIn) Run(in RunInput) error { + var ( + ok = true + value = in.Value.String() + ) + for _, rulePattern := range gstr.SplitAndTrim(in.RulePattern, ",") { + if in.Option.CaseInsensitive { + ok = !strings.EqualFold(value, strings.TrimSpace(rulePattern)) + } else { + ok = strings.Compare(value, strings.TrimSpace(rulePattern)) != 0 + } + if !ok { + return errors.New(in.Message) + } + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_not_regex.go b/util/gvalid/internal/builtin/builtin_not_regex.go new file mode 100644 index 000000000..a2019dea7 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_not_regex.go @@ -0,0 +1,38 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleNotRegex implements `not-regex` rule: +// Value should not match custom regular expression pattern. +// +// Format: not-regex:pattern +type RuleNotRegex struct{} + +func init() { + Register(RuleNotRegex{}) +} + +func (r RuleNotRegex) Name() string { + return "not-regex" +} + +func (r RuleNotRegex) Message() string { + return "The {field} value `{value}` should not be in regex of: {pattern}" +} + +func (r RuleNotRegex) Run(in RunInput) error { + if gregex.IsMatchString(in.RulePattern, in.Value.String()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_passport.go b/util/gvalid/internal/builtin/builtin_passport.go new file mode 100644 index 000000000..742f41dc1 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_passport.go @@ -0,0 +1,43 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RulePassport implements `passport` rule: +// Universal passport format rule: +// Starting with letter, containing only numbers or underscores, length between 6 and 18 +// +// Format: passport +type RulePassport struct{} + +func init() { + Register(RulePassport{}) +} + +func (r RulePassport) Name() string { + return "passport" +} + +func (r RulePassport) Message() string { + return "The {field} value `{value}` is not a valid passport format" +} + +func (r RulePassport) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^[a-zA-Z]{1}\w{5,17}$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_password.go b/util/gvalid/internal/builtin/builtin_password.go new file mode 100644 index 000000000..535d631fb --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_password.go @@ -0,0 +1,39 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RulePassword implements `password` rule: +// Universal password format rule1: +// Containing any visible chars, length between 6 and 18. +// +// Format: password +type RulePassword struct{} + +func init() { + Register(RulePassword{}) +} + +func (r RulePassword) Name() string { + return "password" +} + +func (r RulePassword) Message() string { + return "The {field} value `{value}` is not a valid passport format" +} + +func (r RulePassword) Run(in RunInput) error { + if !gregex.IsMatchString(`^[\w\S]{6,18}$`, in.Value.String()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_password2.go b/util/gvalid/internal/builtin/builtin_password2.go new file mode 100644 index 000000000..4c53d018d --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_password2.go @@ -0,0 +1,43 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RulePassword2 implements `password2` rule: +// Universal password format rule2: +// Must meet password rule1, must contain lower and upper letters and numbers. +// +// Format: password2 +type RulePassword2 struct{} + +func init() { + Register(RulePassword2{}) +} + +func (r RulePassword2) Name() string { + return "password2" +} + +func (r RulePassword2) Message() string { + return "The {field} value `{value}` is not a valid passport format" +} + +func (r RulePassword2) Run(in RunInput) error { + var value = in.Value.String() + if gregex.IsMatchString(`^[\w\S]{6,18}$`, value) && + gregex.IsMatchString(`[a-z]+`, value) && + gregex.IsMatchString(`[A-Z]+`, value) && + gregex.IsMatchString(`\d+`, value) { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_password3.go b/util/gvalid/internal/builtin/builtin_password3.go new file mode 100644 index 000000000..677336511 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_password3.go @@ -0,0 +1,44 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RulePassword3 implements `password3` rule: +// Universal password format rule3: +// Must meet password rule1, must contain lower and upper letters, numbers and special chars. +// +// Format: password3 +type RulePassword3 struct{} + +func init() { + Register(RulePassword3{}) +} + +func (r RulePassword3) Name() string { + return "password3" +} + +func (r RulePassword3) Message() string { + return "The {field} value `{value}` is not a valid passport format" +} + +func (r RulePassword3) Run(in RunInput) error { + var value = in.Value.String() + if gregex.IsMatchString(`^[\w\S]{6,18}$`, value) && + gregex.IsMatchString(`[a-z]+`, value) && + gregex.IsMatchString(`[A-Z]+`, value) && + gregex.IsMatchString(`\d+`, value) && + gregex.IsMatchString(`[^a-zA-Z0-9]+`, value) { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_phone.go b/util/gvalid/internal/builtin/builtin_phone.go new file mode 100644 index 000000000..3e0b5dbae --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_phone.go @@ -0,0 +1,60 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RulePhone implements `phone` rule: +// 1. China Mobile: +// 134, 135, 136, 137, 138, 139, 150, 151, 152, 157, 158, 159, 182, 183, 184, 187, 188, +// 178(4G), 147(Net); +// 172 +// +// 2. China Unicom: +// 130, 131, 132, 155, 156, 185, 186 ,176(4G), 145(Net), 175 +// +// 3. China Telecom: +// 133, 153, 180, 181, 189, 177(4G) +// +// 4. Satelite: +// 1349 +// +// 5. Virtual: +// 170, 173 +// +// 6. 2018: +// 16x, 19x +// +// Format: phone +type RulePhone struct{} + +func init() { + Register(RulePhone{}) +} + +func (r RulePhone) Name() string { + return "phone" +} + +func (r RulePhone) Message() string { + return "The {field} value `{value}` is not a valid phone number" +} + +func (r RulePhone) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^13[\d]{9}$|^14[5,7]{1}\d{8}$|^15[^4]{1}\d{8}$|^16[\d]{9}$|^17[0,2,3,5,6,7,8]{1}\d{8}$|^18[\d]{9}$|^19[\d]{9}$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_phone_loose.go b/util/gvalid/internal/builtin/builtin_phone_loose.go new file mode 100644 index 000000000..4c8bcf3ed --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_phone_loose.go @@ -0,0 +1,45 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RulePhoneLoose implements `phone-loose` rule: +// Loose mobile phone number verification(宽松的手机号验证) +// As long as the 11 digits numbers beginning with +// 13, 14, 15, 16, 17, 18, 19 can pass the verification +// (只要满足 13、14、15、16、17、18、19开头的11位数字都可以通过验证). +// +// Format: phone-loose +type RulePhoneLoose struct{} + +func init() { + Register(RulePhoneLoose{}) +} + +func (r RulePhoneLoose) Name() string { + return "phone-loose" +} + +func (r RulePhoneLoose) Message() string { + return "The {field} value `{value}` is not a valid phone number" +} + +func (r RulePhoneLoose) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^1(3|4|5|6|7|8|9)\d{9}$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_postcode.go b/util/gvalid/internal/builtin/builtin_postcode.go new file mode 100644 index 000000000..8b656f5dc --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_postcode.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RulePostcode implements `postcode` rule: +// Postcode number. +// +// Format: postcode +type RulePostcode struct{} + +func init() { + Register(RulePostcode{}) +} + +func (r RulePostcode) Name() string { + return "postcode" +} + +func (r RulePostcode) Message() string { + return "The {field} value `{value}` is not a valid postcode format" +} + +func (r RulePostcode) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^\d{6}$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_qq.go b/util/gvalid/internal/builtin/builtin_qq.go new file mode 100644 index 000000000..cd3cb9815 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_qq.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleQQ implements `qq` rule: +// Tencent QQ number. +// +// Format: qq +type RuleQQ struct{} + +func init() { + Register(RuleQQ{}) +} + +func (r RuleQQ) Name() string { + return "qq" +} + +func (r RuleQQ) Message() string { + return "The {field} value `{value}` is not a valid QQ number" +} + +func (r RuleQQ) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^[1-9][0-9]{4,}$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_regex.go b/util/gvalid/internal/builtin/builtin_regex.go new file mode 100644 index 000000000..e69089a74 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_regex.go @@ -0,0 +1,38 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleRegex implements `regex` rule: +// Value should match custom regular expression pattern. +// +// Format: regex:pattern +type RuleRegex struct{} + +func init() { + Register(RuleRegex{}) +} + +func (r RuleRegex) Name() string { + return "regex" +} + +func (r RuleRegex) Message() string { + return "The {field} value `{value}` must be in regex of: {pattern}" +} + +func (r RuleRegex) Run(in RunInput) error { + if !gregex.IsMatchString(in.RulePattern, in.Value.String()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_required.go b/util/gvalid/internal/builtin/builtin_required.go new file mode 100644 index 000000000..bf6133215 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_required.go @@ -0,0 +1,49 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "reflect" + + "github.com/gogf/gf/v2/util/gconv" +) + +// RuleRequired implements `required` rule. +// Format: required +type RuleRequired struct{} + +func init() { + Register(RuleRequired{}) +} + +func (r RuleRequired) Name() string { + return "required" +} + +func (r RuleRequired) Message() string { + return "The {field} field is required" +} + +func (r RuleRequired) Run(in RunInput) error { + if isRequiredEmpty(in.Value.Val()) { + return errors.New(in.Message) + } + return nil +} + +func isRequiredEmpty(value interface{}) bool { + reflectValue := reflect.ValueOf(value) + for reflectValue.Kind() == reflect.Ptr { + reflectValue = reflectValue.Elem() + } + switch reflectValue.Kind() { + case reflect.String, reflect.Map, reflect.Array, reflect.Slice: + return reflectValue.Len() == 0 + } + return gconv.String(value) == "" +} diff --git a/util/gvalid/internal/builtin/builtin_required_if.go b/util/gvalid/internal/builtin/builtin_required_if.go new file mode 100644 index 000000000..df116470e --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_required_if.go @@ -0,0 +1,64 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleRequiredIf implements `required-if` rule: +// Required unless all given field and its value are equal. +// +// Format: required-if:field,value,... +// Example: required-if: id,1,age,18 +type RuleRequiredIf struct{} + +func init() { + Register(RuleRequiredIf{}) +} + +func (r RuleRequiredIf) Name() string { + return "required-if" +} + +func (r RuleRequiredIf) Message() string { + return "The {field} field is required" +} + +func (r RuleRequiredIf) Run(in RunInput) error { + var ( + required = false + array = strings.Split(in.RulePattern, ",") + foundValue interface{} + ) + // It supports multiple field and value pairs. + if len(array)%2 == 0 { + for i := 0; i < len(array); { + tk := array[i] + tv := array[i+1] + _, foundValue = gutil.MapPossibleItemByKey(in.Data.Map(), tk) + if in.Option.CaseInsensitive { + required = strings.EqualFold(tv, gconv.String(foundValue)) + } else { + required = strings.Compare(tv, gconv.String(foundValue)) == 0 + } + if required { + break + } + i += 2 + } + } + + if required && isRequiredEmpty(in.Value.Val()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_required_unless.go b/util/gvalid/internal/builtin/builtin_required_unless.go new file mode 100644 index 000000000..b4e122ed6 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_required_unless.go @@ -0,0 +1,64 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleRequiredUnless implements `required-unless` rule: +// Required unless all given field and its value are not equal. +// +// Format: required-unless:field,value,... +// Example: required-unless:id,1,age,18 +type RuleRequiredUnless struct{} + +func init() { + Register(RuleRequiredUnless{}) +} + +func (r RuleRequiredUnless) Name() string { + return "required-unless" +} + +func (r RuleRequiredUnless) Message() string { + return "The {field} field is required" +} + +func (r RuleRequiredUnless) Run(in RunInput) error { + var ( + required = true + array = strings.Split(in.RulePattern, ",") + foundValue interface{} + ) + // It supports multiple field and value pairs. + if len(array)%2 == 0 { + for i := 0; i < len(array); { + tk := array[i] + tv := array[i+1] + _, foundValue = gutil.MapPossibleItemByKey(in.Data.Map(), tk) + if in.Option.CaseInsensitive { + required = !strings.EqualFold(tv, gconv.String(foundValue)) + } else { + required = strings.Compare(tv, gconv.String(foundValue)) != 0 + } + if !required { + break + } + i += 2 + } + } + + if required && isRequiredEmpty(in.Value.Val()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_required_with.go b/util/gvalid/internal/builtin/builtin_required_with.go new file mode 100644 index 000000000..9499c6bb9 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_required_with.go @@ -0,0 +1,54 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/internal/empty" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleRequiredWith implements `required-with` rule: +// Required if any of given fields are not empty. +// +// Format: required-with:field1,field2,... +// Example: required-with:id,name +type RuleRequiredWith struct{} + +func init() { + Register(RuleRequiredWith{}) +} + +func (r RuleRequiredWith) Name() string { + return "required-with" +} + +func (r RuleRequiredWith) Message() string { + return "The {field} field is required" +} + +func (r RuleRequiredWith) Run(in RunInput) error { + var ( + required = false + array = strings.Split(in.RulePattern, ",") + foundValue interface{} + ) + for i := 0; i < len(array); i++ { + _, foundValue = gutil.MapPossibleItemByKey(in.Data.Map(), array[i]) + if !empty.IsEmpty(foundValue) { + required = true + break + } + } + + if required && isRequiredEmpty(in.Value.Val()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_required_with_all.go b/util/gvalid/internal/builtin/builtin_required_with_all.go new file mode 100644 index 000000000..2d850bf79 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_required_with_all.go @@ -0,0 +1,54 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/internal/empty" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleRequiredWithAll implements `required-with-all` rule: +// Required if all given fields are not empty. +// +// Format: required-with-all:field1,field2,... +// Example: required-with-all:id,name +type RuleRequiredWithAll struct{} + +func init() { + Register(RuleRequiredWithAll{}) +} + +func (r RuleRequiredWithAll) Name() string { + return "required-with-all" +} + +func (r RuleRequiredWithAll) Message() string { + return "The {field} field is required" +} + +func (r RuleRequiredWithAll) Run(in RunInput) error { + var ( + required = true + array = strings.Split(in.RulePattern, ",") + foundValue interface{} + ) + for i := 0; i < len(array); i++ { + _, foundValue = gutil.MapPossibleItemByKey(in.Data.Map(), array[i]) + if empty.IsEmpty(foundValue) { + required = false + break + } + } + + if required && isRequiredEmpty(in.Value.Val()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_required_without.go b/util/gvalid/internal/builtin/builtin_required_without.go new file mode 100644 index 000000000..f0efb9b4c --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_required_without.go @@ -0,0 +1,54 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/internal/empty" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleRequiredWithout implements `required-without` rule: +// Required if any of given fields are empty. +// +// Format: required-without:field1,field2,... +// Example: required-without:id,name +type RuleRequiredWithout struct{} + +func init() { + Register(RuleRequiredWithout{}) +} + +func (r RuleRequiredWithout) Name() string { + return "required-without" +} + +func (r RuleRequiredWithout) Message() string { + return "The {field} field is required" +} + +func (r RuleRequiredWithout) Run(in RunInput) error { + var ( + required = false + array = strings.Split(in.RulePattern, ",") + foundValue interface{} + ) + for i := 0; i < len(array); i++ { + _, foundValue = gutil.MapPossibleItemByKey(in.Data.Map(), array[i]) + if empty.IsEmpty(foundValue) { + required = true + break + } + } + + if required && isRequiredEmpty(in.Value.Val()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_required_without_all.go b/util/gvalid/internal/builtin/builtin_required_without_all.go new file mode 100644 index 000000000..ef62b4293 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_required_without_all.go @@ -0,0 +1,54 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/internal/empty" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleRequiredWithoutAll implements `required-without-all` rule: +// Required if all given fields are empty. +// +// Format: required-without-all:field1,field2,... +// Example: required-without-all:id,name +type RuleRequiredWithoutAll struct{} + +func init() { + Register(RuleRequiredWithoutAll{}) +} + +func (r RuleRequiredWithoutAll) Name() string { + return "required-without-all" +} + +func (r RuleRequiredWithoutAll) Message() string { + return "The {field} field is required" +} + +func (r RuleRequiredWithoutAll) Run(in RunInput) error { + var ( + required = true + array = strings.Split(in.RulePattern, ",") + foundValue interface{} + ) + for i := 0; i < len(array); i++ { + _, foundValue = gutil.MapPossibleItemByKey(in.Data.Map(), array[i]) + if !empty.IsEmpty(foundValue) { + required = false + break + } + } + + if required && isRequiredEmpty(in.Value.Val()) { + return errors.New(in.Message) + } + return nil +} diff --git a/util/gvalid/gvalid_validator_rule_resident_id.go b/util/gvalid/internal/builtin/builtin_resident_id.go similarity index 67% rename from util/gvalid/gvalid_validator_rule_resident_id.go rename to util/gvalid/internal/builtin/builtin_resident_id.go index df4555924..3da02e786 100644 --- a/util/gvalid/gvalid_validator_rule_resident_id.go +++ b/util/gvalid/internal/builtin/builtin_resident_id.go @@ -4,15 +4,41 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package gvalid +package builtin import ( + "errors" "strconv" "strings" "github.com/gogf/gf/v2/text/gregex" ) +// RuleResidentId implements `resident-id` rule: +// Resident id number. +// +// Format: resident-id +type RuleResidentId struct{} + +func init() { + Register(RuleResidentId{}) +} + +func (r RuleResidentId) Name() string { + return "resident-id" +} + +func (r RuleResidentId) Message() string { + return "The {field} value `{value}` is not a valid resident id number" +} + +func (r RuleResidentId) Run(in RunInput) error { + if r.checkResidentId(in.Value.String()) { + return nil + } + return errors.New(in.Message) +} + // checkResidentId checks whether given id a china resident id number. // // xxxxxx yyyy MM dd 375 0 十八位 @@ -33,7 +59,7 @@ import ( // // 总: // (^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$) -func (v *Validator) checkResidentId(id string) bool { +func (r RuleResidentId) checkResidentId(id string) bool { id = strings.ToUpper(strings.TrimSpace(id)) if len(id) != 18 { return false @@ -55,5 +81,8 @@ func (v *Validator) checkResidentId(id string) bool { return false } - return gregex.IsMatchString(`(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)`, id) + return gregex.IsMatchString( + `(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)`, + id, + ) } diff --git a/util/gvalid/internal/builtin/builtin_same.go b/util/gvalid/internal/builtin/builtin_same.go new file mode 100644 index 000000000..2ac234ddb --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_same.go @@ -0,0 +1,56 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strings" + + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/gutil" +) + +// RuleSame implements `same` rule: +// Value should be the same as value of field. +// +// Format: same:field +type RuleSame struct{} + +func init() { + Register(RuleSame{}) +} + +func (r RuleSame) Name() string { + return "same" +} + +func (r RuleSame) Message() string { + return "The {field} value `{value}` must be the same as field {field1} value `{value1}`" +} + +func (r RuleSame) Run(in RunInput) error { + var ( + ok bool + value = in.Value.String() + ) + fieldName, fieldValue := gutil.MapPossibleItemByKey(in.Data.Map(), in.RulePattern) + if fieldValue != nil { + if in.Option.CaseInsensitive { + ok = strings.EqualFold(value, gconv.String(fieldValue)) + } else { + ok = strings.Compare(value, gconv.String(fieldValue)) == 0 + } + } + if !ok { + return errors.New(gstr.ReplaceByMap(in.Message, map[string]string{ + "{field1}": fieldName, + "{value1}": gconv.String(fieldValue), + })) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_size.go b/util/gvalid/internal/builtin/builtin_size.go new file mode 100644 index 000000000..becf32b79 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_size.go @@ -0,0 +1,46 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + "strconv" + "strings" + + "github.com/gogf/gf/v2/util/gconv" +) + +// RuleSize implements `size` rule: +// Length must be :size. +// The length is calculated using unicode string, which means one chinese character or letter both has the length of 1. +// +// Format: size:size +type RuleSize struct{} + +func init() { + Register(RuleSize{}) +} + +func (r RuleSize) Name() string { + return "size" +} + +func (r RuleSize) Message() string { + return "The {field} value `{value}` length must be {size}" +} + +func (r RuleSize) Run(in RunInput) error { + var ( + valueRunes = gconv.Runes(in.Value.String()) + valueLen = len(valueRunes) + ) + size, err := strconv.Atoi(in.RulePattern) + if valueLen != size || err != nil { + return errors.New(strings.Replace(in.Message, "{size}", strconv.Itoa(size), -1)) + } + return nil +} diff --git a/util/gvalid/internal/builtin/builtin_telephone.go b/util/gvalid/internal/builtin/builtin_telephone.go new file mode 100644 index 000000000..1adb7a615 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_telephone.go @@ -0,0 +1,47 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleTelephone implements `telephone` rule: +// "XXXX-XXXXXXX" +// "XXXX-XXXXXXXX" +// "XXX-XXXXXXX" +// "XXX-XXXXXXXX" +// "XXXXXXX" +// "XXXXXXXX" +// +// Format: telephone +type RuleTelephone struct{} + +func init() { + Register(RuleTelephone{}) +} + +func (r RuleTelephone) Name() string { + return "telephone" +} + +func (r RuleTelephone) Message() string { + return "The {field} value `{value}` is not a valid telephone number" +} + +func (r RuleTelephone) Run(in RunInput) error { + ok := gregex.IsMatchString( + `^((\d{3,4})|\d{3,4}-)?\d{7,8}$`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/internal/builtin/builtin_url.go b/util/gvalid/internal/builtin/builtin_url.go new file mode 100644 index 000000000..cb4e356a0 --- /dev/null +++ b/util/gvalid/internal/builtin/builtin_url.go @@ -0,0 +1,42 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package builtin + +import ( + "errors" + + "github.com/gogf/gf/v2/text/gregex" +) + +// RuleUrl implements `url` rule: +// URL. +// +// Format: url +type RuleUrl struct{} + +func init() { + Register(RuleUrl{}) +} + +func (r RuleUrl) Name() string { + return "url" +} + +func (r RuleUrl) Message() string { + return "The {field} value `{value}` is not a valid URL address" +} + +func (r RuleUrl) Run(in RunInput) error { + ok := gregex.IsMatchString( + `(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]`, + in.Value.String(), + ) + if ok { + return nil + } + return errors.New(in.Message) +} diff --git a/util/gvalid/testdata/i18n/cn/validation.toml b/util/gvalid/testdata/i18n/cn/validation.toml index 851f09e30..d95bfc215 100644 --- a/util/gvalid/testdata/i18n/cn/validation.toml +++ b/util/gvalid/testdata/i18n/cn/validation.toml @@ -1,49 +1,49 @@ -"gf.gvalid.rule.required" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-if" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-unless" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-with" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-with-all" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-without" = "{attribute}字段不能为空" -"gf.gvalid.rule.required-without-all" = "{attribute}字段不能为空" -"gf.gvalid.rule.date" = "{attribute}字段值`{value}`日期格式不满足Y-m-d格式,例如: 2001-02-03" -"gf.gvalid.rule.datetime" = "{attribute}字段值`{value}`日期格式不满足Y-m-d H:i:s格式,例如: 2001-02-03 12:00:00" -"gf.gvalid.rule.date-format" = "{attribute}字段值`{value}`日期格式不满足{format}" -"gf.gvalid.rule.email" = "{attribute}字段值`{value}`邮箱地址格式不正确" -"gf.gvalid.rule.phone" = "{attribute}字段值`{value}`手机号码格式不正确" -"gf.gvalid.rule.phone-loose" = "{attribute}字段值`{value}`手机号码格式不正确" -"gf.gvalid.rule.telephone" = "{attribute}字段值`{value}`电话号码格式不正确" -"gf.gvalid.rule.passport" = "{attribute}字段值`{value}`账号格式不合法,必需以字母开头,只能包含字母、数字和下划线,长度在6~18之间" -"gf.gvalid.rule.password" = "{attribute}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符" -"gf.gvalid.rule.password2" = "{attribute}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母和数字" -"gf.gvalid.rule.password3" = "{attribute}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母、数字和特殊字符" -"gf.gvalid.rule.postcode" = "{attribute}字段值`{value}`邮政编码不正确" -"gf.gvalid.rule.resident-id" = "{attribute}字段值`{value}`身份证号码格式不正确" -"gf.gvalid.rule.bank-card" = "{attribute}字段值`{value}`银行卡号格式不正确" -"gf.gvalid.rule.qq" = "{attribute}字段值`{value}`QQ号码格式不正确" -"gf.gvalid.rule.ip" = "{attribute}字段值`{value}`IP地址格式不正确" -"gf.gvalid.rule.ipv4" = "{attribute}字段值`{value}`IPv4地址格式不正确" -"gf.gvalid.rule.ipv6" = "{attribute}字段值`{value}`IPv6地址格式不正确" -"gf.gvalid.rule.mac" = "{attribute}字段值`{value}`MAC地址格式不正确" -"gf.gvalid.rule.url" = "{attribute}字段值`{value}`URL地址格式不正确" -"gf.gvalid.rule.domain" = "{attribute}字段值`{value}`域名格式不正确" -"gf.gvalid.rule.length" = "{attribute}字段值`{value}`字段长度应当为{min}到{max}个字符" -"gf.gvalid.rule.min-length" = "{attribute}字段值`{value}`字段最小长度应当为{min}" -"gf.gvalid.rule.max-length" = "{attribute}字段值`{value}`字段最大长度应当为{max}" -"gf.gvalid.rule.size" = "{attribute}字段值`{value}`字段长度必须应当为{size}" -"gf.gvalid.rule.between" = "{attribute}字段值`{value}`字段大小应当为{min}到{max}" -"gf.gvalid.rule.min" = "{attribute}字段值`{value}`字段最小值应当为{min}" -"gf.gvalid.rule.max" = "{attribute}字段值`{value}`字段最大值应当为{max}" -"gf.gvalid.rule.json" = "{attribute}字段值`{value}`字段应当为JSON格式" -"gf.gvalid.rule.xml" = "{attribute}字段值`{value}`字段应当为XML格式" -"gf.gvalid.rule.array" = "{attribute}字段值`{value}`字段应当为数组" -"gf.gvalid.rule.integer" = "{attribute}字段值`{value}`字段应当为整数" -"gf.gvalid.rule.float" = "{attribute}字段值`{value}`字段应当为浮点数" -"gf.gvalid.rule.boolean" = "{attribute}字段值`{value}`字段应当为布尔值" -"gf.gvalid.rule.same" = "{attribute}字段值`{value}`字段值必须和{field}相同" -"gf.gvalid.rule.different" = "{attribute}字段值`{value}`字段值不能与{field}相同" -"gf.gvalid.rule.in" = "{attribute}字段值`{value}`字段值应当满足取值范围:{pattern}" -"gf.gvalid.rule.not-in" = "{attribute}字段值`{value}`字段值不应当满足取值范围:{pattern}" -"gf.gvalid.rule.regex" = "{attribute}字段值`{value}`字段值不满足规则:{pattern}" -"gf.gvalid.rule.__default__" = "{attribute}字段值`{value}`字段值不合法" +"gf.gvalid.rule.required" = "{field}字段不能为空" +"gf.gvalid.rule.required-if" = "{field}字段不能为空" +"gf.gvalid.rule.required-unless" = "{field}字段不能为空" +"gf.gvalid.rule.required-with" = "{field}字段不能为空" +"gf.gvalid.rule.required-with-all" = "{field}字段不能为空" +"gf.gvalid.rule.required-without" = "{field}字段不能为空" +"gf.gvalid.rule.required-without-all" = "{field}字段不能为空" +"gf.gvalid.rule.date" = "{field}字段值`{value}`日期格式不满足Y-m-d格式,例如: 2001-02-03" +"gf.gvalid.rule.datetime" = "{field}字段值`{value}`日期格式不满足Y-m-d H:i:s格式,例如: 2001-02-03 12:00:00" +"gf.gvalid.rule.date-format" = "{field}字段值`{value}`日期格式不满足{format}" +"gf.gvalid.rule.email" = "{field}字段值`{value}`邮箱地址格式不正确" +"gf.gvalid.rule.phone" = "{field}字段值`{value}`手机号码格式不正确" +"gf.gvalid.rule.phone-loose" = "{field}字段值`{value}`手机号码格式不正确" +"gf.gvalid.rule.telephone" = "{field}字段值`{value}`电话号码格式不正确" +"gf.gvalid.rule.passport" = "{field}字段值`{value}`账号格式不合法,必需以字母开头,只能包含字母、数字和下划线,长度在6~18之间" +"gf.gvalid.rule.password" = "{field}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符" +"gf.gvalid.rule.password2" = "{field}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母和数字" +"gf.gvalid.rule.password3" = "{field}字段值`{value}`密码格式不合法,密码格式为任意6-18位的可见字符,必须包含大小写字母、数字和特殊字符" +"gf.gvalid.rule.postcode" = "{field}字段值`{value}`邮政编码不正确" +"gf.gvalid.rule.resident-id" = "{field}字段值`{value}`身份证号码格式不正确" +"gf.gvalid.rule.bank-card" = "{field}字段值`{value}`银行卡号格式不正确" +"gf.gvalid.rule.qq" = "{field}字段值`{value}`QQ号码格式不正确" +"gf.gvalid.rule.ip" = "{field}字段值`{value}`IP地址格式不正确" +"gf.gvalid.rule.ipv4" = "{field}字段值`{value}`IPv4地址格式不正确" +"gf.gvalid.rule.ipv6" = "{field}字段值`{value}`IPv6地址格式不正确" +"gf.gvalid.rule.mac" = "{field}字段值`{value}`MAC地址格式不正确" +"gf.gvalid.rule.url" = "{field}字段值`{value}`URL地址格式不正确" +"gf.gvalid.rule.domain" = "{field}字段值`{value}`域名格式不正确" +"gf.gvalid.rule.length" = "{field}字段值`{value}`字段长度应当为{min}到{max}个字符" +"gf.gvalid.rule.min-length" = "{field}字段值`{value}`字段最小长度应当为{min}" +"gf.gvalid.rule.max-length" = "{field}字段值`{value}`字段最大长度应当为{max}" +"gf.gvalid.rule.size" = "{field}字段值`{value}`字段长度必须应当为{size}" +"gf.gvalid.rule.between" = "{field}字段值`{value}`字段大小应当为{min}到{max}" +"gf.gvalid.rule.min" = "{field}字段值`{value}`字段最小值应当为{min}" +"gf.gvalid.rule.max" = "{field}字段值`{value}`字段最大值应当为{max}" +"gf.gvalid.rule.json" = "{field}字段值`{value}`字段应当为JSON格式" +"gf.gvalid.rule.xml" = "{field}字段值`{value}`字段应当为XML格式" +"gf.gvalid.rule.array" = "{field}字段值`{value}`字段应当为数组" +"gf.gvalid.rule.integer" = "{field}字段值`{value}`字段应当为整数" +"gf.gvalid.rule.float" = "{field}字段值`{value}`字段应当为浮点数" +"gf.gvalid.rule.boolean" = "{field}字段值`{value}`字段应当为布尔值" +"gf.gvalid.rule.same" = "{field}字段值`{value}`字段值必须和{field}相同" +"gf.gvalid.rule.different" = "{field}字段值`{value}`字段值不能与{field}相同" +"gf.gvalid.rule.in" = "{field}字段值`{value}`字段值应当满足取值范围:{pattern}" +"gf.gvalid.rule.not-in" = "{field}字段值`{value}`字段值不应当满足取值范围:{pattern}" +"gf.gvalid.rule.regex" = "{field}字段值`{value}`字段值不满足规则:{pattern}" +"gf.gvalid.rule.__default__" = "{field}字段值`{value}`字段值不合法" "CustomMessage" = "自定义错误" "project id must between {min}, {max}" = "项目ID必须大于等于{min}并且要小于等于{max}" \ No newline at end of file diff --git a/util/gvalid/testdata/i18n/en/validation.toml b/util/gvalid/testdata/i18n/en/validation.toml index 90f1a7f69..051d9ed6d 100644 --- a/util/gvalid/testdata/i18n/en/validation.toml +++ b/util/gvalid/testdata/i18n/en/validation.toml @@ -1,45 +1,45 @@ -"gf.gvalid.rule.required" = "The {attribute} field is required" -"gf.gvalid.rule.required-if" = "The {attribute} field is required" -"gf.gvalid.rule.required-unless" = "The {attribute} field is required" -"gf.gvalid.rule.required-with" = "The {attribute} field is required" -"gf.gvalid.rule.required-with-all" = "The {attribute} field is required" -"gf.gvalid.rule.required-without" = "The {attribute} field is required" -"gf.gvalid.rule.required-without-all" = "The {attribute} field is required" -"gf.gvalid.rule.date" = "The {attribute} value `{value}` is not a valid date" -"gf.gvalid.rule.datetime" = "The {attribute} value `{value}` is not a valid datetime" -"gf.gvalid.rule.date-format" = "The {attribute} value `{value}` does not match the format: {pattern}" -"gf.gvalid.rule.email" = "The {attribute} value `{value}` is not a valid email address" -"gf.gvalid.rule.phone" = "The {attribute} value `{value}` is not a valid phone number" -"gf.gvalid.rule.telephone" = "The {attribute} value `{value}` is not a valid telephone number" -"gf.gvalid.rule.passport" = "The {attribute} value `{value}` is not a valid passport format" -"gf.gvalid.rule.password" = "The {attribute} value `{value}` is not a valid password format" -"gf.gvalid.rule.password2" = "The {attribute} value `{value}` is not a valid password format" -"gf.gvalid.rule.password3" = "The {attribute} value `{value}` is not a valid password format" -"gf.gvalid.rule.postcode" = "The {attribute} value `{value}` is not a valid postcode format" -"gf.gvalid.rule.resident-id" = "The {attribute} value `{value}` is not a valid resident id number" -"gf.gvalid.rule.bank-card" = "The {attribute} value `{value}` is not a valid bank card number" -"gf.gvalid.rule.qq" = "The {attribute} value `{value}` is not a valid QQ number" -"gf.gvalid.rule.ip" = "The {attribute} value `{value}` is not a valid IP address" -"gf.gvalid.rule.ipv4" = "The {attribute} value `{value}` is not a valid IPv4 address" -"gf.gvalid.rule.ipv6" = "The {attribute} value `{value}` is not a valid IPv6 address" -"gf.gvalid.rule.mac" = "The {attribute} value `{value}` is not a valid MAC address" -"gf.gvalid.rule.url" = "The {attribute} value `{value}` is not a valid URL address" -"gf.gvalid.rule.domain" = "The {attribute} value `{value}` is not a valid domain format" -"gf.gvalid.rule.length" = "The {attribute} value `{value}` length must be between {min} and {max}" -"gf.gvalid.rule.min-length" = "The {attribute} value `{value}` length must be equal or greater than {min}" -"gf.gvalid.rule.max-length" = "The {attribute} value `{value}` length must be equal or lesser than {max}" -"gf.gvalid.rule.size" = "The {attribute} value `{value}` length must be {size}" -"gf.gvalid.rule.between" = "The {attribute} value `{value}` must be between {min} and {max}" -"gf.gvalid.rule.min" = "The {attribute} value `{value}` must be equal or greater than {min}" -"gf.gvalid.rule.max" = "The {attribute} value `{value}` must be equal or lesser than {max}" -"gf.gvalid.rule.json" = "The {attribute} value `{value}` is not a valid JSON string" -"gf.gvalid.rule.xml" = "The {attribute} value `{value}` is not a valid XML string" -"gf.gvalid.rule.array" = "The {attribute} value `{value}` is not an array" -"gf.gvalid.rule.integer" = "The {attribute} value `{value}` is not an integer" -"gf.gvalid.rule.boolean" = "The {attribute} value `{value}` field must be true or false" -"gf.gvalid.rule.same" = "The {attribute} value `{value}` must be the same as field {pattern}" -"gf.gvalid.rule.different" = "The {attribute} value `{value}` must be different from field {pattern}" -"gf.gvalid.rule.in" = "The {attribute} value `{value}` is not in acceptable range: {pattern}" -"gf.gvalid.rule.not-in" = "The {attribute} value `{value}` must not be in range: {pattern}" -"gf.gvalid.rule.regex" = "The {attribute} value `{value}` must be in regex of: {pattern}" +"gf.gvalid.rule.required" = "The {field} field is required" +"gf.gvalid.rule.required-if" = "The {field} field is required" +"gf.gvalid.rule.required-unless" = "The {field} field is required" +"gf.gvalid.rule.required-with" = "The {field} field is required" +"gf.gvalid.rule.required-with-all" = "The {field} field is required" +"gf.gvalid.rule.required-without" = "The {field} field is required" +"gf.gvalid.rule.required-without-all" = "The {field} field is required" +"gf.gvalid.rule.date" = "The {field} value `{value}` is not a valid date" +"gf.gvalid.rule.datetime" = "The {field} value `{value}` is not a valid datetime" +"gf.gvalid.rule.date-format" = "The {field} value `{value}` does not match the format: {pattern}" +"gf.gvalid.rule.email" = "The {field} value `{value}` is not a valid email address" +"gf.gvalid.rule.phone" = "The {field} value `{value}` is not a valid phone number" +"gf.gvalid.rule.telephone" = "The {field} value `{value}` is not a valid telephone number" +"gf.gvalid.rule.passport" = "The {field} value `{value}` is not a valid passport format" +"gf.gvalid.rule.password" = "The {field} value `{value}` is not a valid password format" +"gf.gvalid.rule.password2" = "The {field} value `{value}` is not a valid password format" +"gf.gvalid.rule.password3" = "The {field} value `{value}` is not a valid password format" +"gf.gvalid.rule.postcode" = "The {field} value `{value}` is not a valid postcode format" +"gf.gvalid.rule.resident-id" = "The {field} value `{value}` is not a valid resident id number" +"gf.gvalid.rule.bank-card" = "The {field} value `{value}` is not a valid bank card number" +"gf.gvalid.rule.qq" = "The {field} value `{value}` is not a valid QQ number" +"gf.gvalid.rule.ip" = "The {field} value `{value}` is not a valid IP address" +"gf.gvalid.rule.ipv4" = "The {field} value `{value}` is not a valid IPv4 address" +"gf.gvalid.rule.ipv6" = "The {field} value `{value}` is not a valid IPv6 address" +"gf.gvalid.rule.mac" = "The {field} value `{value}` is not a valid MAC address" +"gf.gvalid.rule.url" = "The {field} value `{value}` is not a valid URL address" +"gf.gvalid.rule.domain" = "The {field} value `{value}` is not a valid domain format" +"gf.gvalid.rule.length" = "The {field} value `{value}` length must be between {min} and {max}" +"gf.gvalid.rule.min-length" = "The {field} value `{value}` length must be equal or greater than {min}" +"gf.gvalid.rule.max-length" = "The {field} value `{value}` length must be equal or lesser than {max}" +"gf.gvalid.rule.size" = "The {field} value `{value}` length must be {size}" +"gf.gvalid.rule.between" = "The {field} value `{value}` must be between {min} and {max}" +"gf.gvalid.rule.min" = "The {field} value `{value}` must be equal or greater than {min}" +"gf.gvalid.rule.max" = "The {field} value `{value}` must be equal or lesser than {max}" +"gf.gvalid.rule.json" = "The {field} value `{value}` is not a valid JSON string" +"gf.gvalid.rule.xml" = "The {field} value `{value}` is not a valid XML string" +"gf.gvalid.rule.array" = "The {field} value `{value}` is not an array" +"gf.gvalid.rule.integer" = "The {field} value `{value}` is not an integer" +"gf.gvalid.rule.boolean" = "The {field} value `{value}` field must be true or false" +"gf.gvalid.rule.same" = "The {field} value `{value}` must be the same as field {pattern}" +"gf.gvalid.rule.different" = "The {field} value `{value}` must be different from field {pattern}" +"gf.gvalid.rule.in" = "The {field} value `{value}` is not in acceptable range: {pattern}" +"gf.gvalid.rule.not-in" = "The {field} value `{value}` must not be in range: {pattern}" +"gf.gvalid.rule.regex" = "The {field} value `{value}` must be in regex of: {pattern}" "gf.gvalid.rule.gf.gvalid.rule.__default__" = "The :attribute value `:value` is invalid" \ No newline at end of file diff --git a/version.go b/version.go index 1192ef9cc..0c9540a38 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v2.1.4" +const VERSION = "v2.2.0" const AUTHORS = "john"