Update dependencies

This commit is contained in:
Ingo Oppermann 2024-04-29 16:37:17 +02:00
parent 4dab7b8e6c
commit 24cc87ae7e
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
1548 changed files with 70419 additions and 68575 deletions

128
go.mod
View File

@ -1,133 +1,127 @@
module github.com/datarhei/core/v16
go 1.18
go 1.21
toolchain go1.22.1
require (
github.com/99designs/gqlgen v0.17.36
github.com/99designs/gqlgen v0.17.45
github.com/Masterminds/semver/v3 v3.2.1
github.com/adhocore/gronx v1.6.5
github.com/adhocore/gronx v1.8.1
github.com/andybalholm/brotli v1.1.0
github.com/atrox/haikunatorgo/v2 v2.0.1
github.com/caddyserver/certmagic v0.19.2
github.com/casbin/casbin/v2 v2.77.2
github.com/caddyserver/certmagic v0.20.0
github.com/casbin/casbin/v2 v2.88.0
github.com/datarhei/core-client-go/v16 v16.11.1-0.20240424105158-86a7f261b92c
github.com/datarhei/gosrt v0.5.4
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a
github.com/datarhei/gosrt v0.6.0
github.com/datarhei/joy4 v0.0.0-20240229100136-43bcaf8ef5e7
github.com/fujiwara/shapeio v1.0.0
github.com/go-playground/validator/v10 v10.15.3
github.com/go-playground/validator/v10 v10.19.0
github.com/gobwas/glob v0.2.3
github.com/goccy/go-json v0.10.2
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/gops v0.3.28
github.com/google/uuid v1.3.1
github.com/hashicorp/go-hclog v1.5.0
github.com/hashicorp/raft v1.5.0
github.com/hashicorp/raft-boltdb/v2 v2.2.2
github.com/google/uuid v1.6.0
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/raft v1.6.1
github.com/hashicorp/raft-boltdb/v2 v2.3.0
github.com/invopop/jsonschema v0.4.0
github.com/joho/godotenv v1.5.1
github.com/klauspost/compress v1.16.7
github.com/klauspost/cpuid/v2 v2.2.5
github.com/labstack/echo/v4 v4.11.1
github.com/klauspost/compress v1.17.8
github.com/klauspost/cpuid/v2 v2.2.7
github.com/labstack/echo/v4 v4.12.0
github.com/lestrrat-go/strftime v1.0.6
github.com/lithammer/shortuuid/v4 v4.0.0
github.com/mattn/go-isatty v0.0.19
github.com/minio/minio-go/v7 v7.0.63
github.com/mattn/go-isatty v0.0.20
github.com/minio/minio-go/v7 v7.0.70
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3
github.com/prometheus/client_golang v1.16.0
github.com/prometheus/client_golang v1.19.0
github.com/puzpuzpuz/xsync/v3 v3.1.0
github.com/shirou/gopsutil/v3 v3.23.8
github.com/stretchr/testify v1.8.4
github.com/shirou/gopsutil/v3 v3.24.3
github.com/stretchr/testify v1.9.0
github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/swag v1.16.2
github.com/vektah/gqlparser/v2 v2.5.8
github.com/swaggo/swag v1.16.3
github.com/vektah/gqlparser/v2 v2.5.11
github.com/xeipuuv/gojsonschema v1.2.0
go.etcd.io/bbolt v1.3.7
go.etcd.io/bbolt v1.3.9
go.uber.org/automaxprocs v1.5.3
go.uber.org/zap v1.25.0
golang.org/x/crypto v0.12.0
golang.org/x/mod v0.12.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.22.0
golang.org/x/mod v0.17.0
)
//replace github.com/datarhei/core-client-go/v16 => ../core-client-go
require (
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/boltdb/bolt v1.3.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/casbin/govaluate v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack v0.5.5 // indirect
github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.6 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/iancoleman/orderedmap v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mholt/acmez v1.2.0 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/miekg/dns v1.1.59 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.53.0 // indirect
github.com/prometheus/procfs v0.14.0 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sosodev/duration v1.3.0 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/tidwall/gjson v1.16.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/urfave/cli/v2 v2.25.5 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/urfave/cli/v2 v2.27.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.14.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.12.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.20.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

308
go.sum
View File

@ -1,15 +1,14 @@
github.com/99designs/gqlgen v0.17.36 h1:u/o/rv2SZ9s5280dyUOOrkpIIkr/7kITMXYD3rkJ9go=
github.com/99designs/gqlgen v0.17.36/go.mod h1:6RdyY8puhCoWAQVr2qzF2OMVfudQzc8ACxzpzluoQm4=
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/99designs/gqlgen v0.17.45 h1:bH0AH67vIJo8JKNKPJP+pOPpQhZeuVRQLf53dKIpDik=
github.com/99designs/gqlgen v0.17.45/go.mod h1:Bas0XQ+Jiu/Xm5E33jC8sES3G+iC2esHBMXcq0fUPs0=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/adhocore/gronx v1.6.5 h1:/pryEagBKz3WqUgpgvtL51eBN2rJLXowuW7rpS+jrew=
github.com/adhocore/gronx v1.6.5/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
github.com/adhocore/gronx v1.8.1 h1:F2mLTG5sB11z7vplwD4iydz3YCEjstSfYmCrdSm3t6A=
github.com/adhocore/gronx v1.8.1/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -20,14 +19,14 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/atrox/haikunatorgo/v2 v2.0.1 h1:FCVx2KL2YvZtI1rI9WeEHxeLRrKGr0Dd4wfCJiUXupc=
github.com/atrox/haikunatorgo/v2 v2.0.1/go.mod h1:BBQmx2o+1Z5poziaHRgddAZKOpijwfKdAmMnSYlFK70=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYhJeJ2aZxADI2tGADS15AzIF8MQ8XAhT4=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -36,24 +35,26 @@ 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/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0=
github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
github.com/casbin/casbin/v2 v2.88.0 h1:JFHId/aIFvNvPnTwUP+tTtVAjSh3eidslFzy+5LpSeU=
github.com/casbin/casbin/v2 v2.88.0/go.mod h1:jX8uoN4veP85O/n2674r2qtfSXI6myvxW85f6TH50fw=
github.com/casbin/govaluate v1.1.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/casbin/govaluate v1.1.1 h1:J1rFKIBhiC5xr0APd5HP6rDL+xt+BRoyq1pa4o2i/5c=
github.com/casbin/govaluate v1.1.1/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20240424105158-86a7f261b92c h1:RIzMclqmSYwpMZxyW7nLg0XyKjY6prQWcuIdgm97U8o=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20240424105158-86a7f261b92c/go.mod h1:7XrUOUlB165Gs8JUE4lzVuNve6HW90Yz3/+lTY2EV4A=
github.com/datarhei/gosrt v0.5.4 h1:dE3mmSB+n1GeviGM8xQAW3+UD3mKeFmd84iefDul5Vs=
github.com/datarhei/gosrt v0.5.4/go.mod h1:MiUCwCG+LzFMzLM/kTA+3wiTtlnkVvGbW/F0XzyhtG8=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
github.com/datarhei/gosrt v0.6.0 h1:HrrXAw90V78ok4WMIhX6se1aTHPCn82Sg2hj+PhdmGc=
github.com/datarhei/gosrt v0.6.0/go.mod h1:fsOWdLSHUHShHjgi/46h6wjtdQrtnSdAQFnlas8ONxs=
github.com/datarhei/joy4 v0.0.0-20240229100136-43bcaf8ef5e7 h1:MG5XQMTTDPcuvvRzc1c37QbwgDbYPhKmPFo9gSaPdBE=
github.com/datarhei/joy4 v0.0.0-20240229100136-43bcaf8ef5e7/go.mod h1:Jcw/6jZDQQmPx8A7INEkXmuEF7E9jjBbSTfVSLwmiQw=
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=
@ -63,12 +64,12 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fujiwara/shapeio v1.0.0 h1:xG5D9oNqCSUUbryZ/jQV3cqe1v2suEjwPIcEg1gKM8M=
github.com/fujiwara/shapeio v1.0.0/go.mod h1:LmEmu6L/8jetyj1oewewFb7bZCNRwE7wLCUNzDLaLVA=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -78,28 +79,22 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo=
github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
@ -110,54 +105,51 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gops v0.3.28 h1:2Xr57tqKAmQYRAfG12E+yLcoa2Y42UJo2lOrUFL9ark=
github.com/google/gops v0.3.28/go.mod h1:6f6+Nl8LcHrzJwi8+p0ii+vmBFSlB4f8cOOkTJ7sk4c=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0=
github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM=
github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM=
github.com/hashicorp/raft v1.5.0 h1:uNs9EfJ4FwiArZRxxfd/dQ5d33nV31/CdCHArH89hT8=
github.com/hashicorp/raft v1.5.0/go.mod h1:pKHB2mf/Y25u3AHNSXVRv+yT+WAnmeTX0BwVppVQV+M=
github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea h1:RxcPJuutPRM8PUOyiweMmkuNO+RJyfy2jds2gfvgNmU=
github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea/go.mod h1:qRd6nFJYYS6Iqnc/8HcUmko2/2Gw8qTFEmxDLii6W5I=
github.com/hashicorp/raft-boltdb/v2 v2.2.2 h1:rlkPtOllgIcKLxVT4nutqlTH2NRFn+tO1wwZk/4Dxqw=
github.com/hashicorp/raft-boltdb/v2 v2.2.2/go.mod h1:N8YgaZgNJLpZC+h+by7vDu5rzsRgONThTEeUS3zWbfY=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/raft v1.6.1 h1:v/jm5fcYHvVkL0akByAp+IDdDSzCNCGhdO6VdB56HIM=
github.com/hashicorp/raft v1.6.1/go.mod h1:N1sKh6Vn47mrWvEArQgILTyng8GoDRNYlgKyK7PMjs0=
github.com/hashicorp/raft-boltdb v0.0.0-20230125174641-2a8082862702 h1:RLKEcCuKcZ+qp2VlaaZsYZfLOmIiuJNpEi48Rl8u9cQ=
github.com/hashicorp/raft-boltdb v0.0.0-20230125174641-2a8082862702/go.mod h1:nTakvJ4XYq45UXtn0DbwR4aU9ZdjlnIenpbs6Cd+FM0=
github.com/hashicorp/raft-boltdb/v2 v2.3.0 h1:fPpQR1iGEVYjZ2OELvUHX600VAK5qmdnDEv3eXOwZUA=
github.com/hashicorp/raft-boltdb/v2 v2.3.0/go.mod h1:YHukhB04ChJsLHLJEUD6vjFyLX2L3dsX3wPBZcX4tmc=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
github.com/iancoleman/orderedmap v0.2.0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
@ -169,80 +161,66 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=
github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 h1:1KuuSOy4ZNgW0KA2oYIngXVFhQcXxhLqCVK7cBcldkk=
github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ=
github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/minio/minio-go/v7 v7.0.70 h1:1u9NtMgfK1U42kUxcsl5v0yj6TEOPR497OAQxpJnn2g=
github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -253,97 +231,87 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3 h1:Y7qCvg282QmlyrVQuL2fgGwebuw7zvfnRym09r+dUGc=
github.com/prep/average v0.0.0-20200506183628-d26c465f48c3/go.mod h1:0ZE5gcyWKS151WBDIpmLshHY0l+3edpuKnBUWVVbWKk=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE=
github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/prometheus/procfs v0.14.0 h1:Lw4VdGGoKEZilJsayHf0B+9YgLGREba2C6xr+Fdfq6s=
github.com/prometheus/procfs v0.14.0/go.mod h1:XL+Iwz8k8ZabyZfMFHPiilCniixqQarAy5Mu67pHlNQ=
github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4=
github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE=
github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ=
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sosodev/duration v1.3.0 h1:g3E6mto+hFdA2uZXeNDYff8LYeg7v5D4YKP/Ng/NUkE=
github.com/sosodev/duration v1.3.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
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.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk=
github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc=
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4=
github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -353,41 +321,41 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -395,41 +363,35 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
@ -441,7 +403,5 @@ gopkg.in/yaml.v2 v2.2.5/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=

View File

@ -16,3 +16,5 @@
*.out
gqlgen
*.exe
node_modules

View File

@ -5,10 +5,555 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
<a name="unreleased"></a>
## [Unreleased](https://github.com/99designs/gqlgen/compare/v0.17.35...HEAD)
## [Unreleased](https://github.com/99designs/gqlgen/compare/v0.17.42...HEAD)
<!-- end of if -->
<!-- end of CommitGroups -->
<a name="v0.17.42"></a>
## [v0.17.42](https://github.com/99designs/gqlgen/compare/v0.17.41...v0.17.42) - 2023-12-29
- <a href="https://github.com/99designs/gqlgen/commit/7bf0c223aec642d086793698bc2a0d1a6fdb09b4"><tt>7bf0c223</tt></a> release v0.17.42
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/c811d47ec498bdd50591f163e7d23a7524e98280"><tt>c811d47e</tt></a> fix: avoid panic from tracing on bad request (<a href="https://github.com/99designs/gqlgen/pull/2871">#2871</a>)</summary>
This fixes a panic which arises from the tracing components when a request has some defect which results in an error when creating the operation context. The transports consistently handle this by calling `DispatchError(graphql.WithOperationContext(ctx, rc), err)` where `rc` is the OperationContext which was not correctly constructed. This seems dangerous, because middleware may assume that if there in an `OperationContext` in the `context.Context` than they are being invoked on a normal codepath and can assume their other interceptors have been invoked in the normal order. Also, using a value returned by a function which also returned a non-nil error is very unusual. However, I have no idea what the impact of changing that dangerous behavior in the transports would be, so I opted to make the tracing component more resilient instead.
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/13bb415268dda837690835e65e331746c8df892b"><tt>13bb4152</tt></a> fix for entity interfce code gen with related test (<a href="https://github.com/99designs/gqlgen/pull/2868">#2868</a>)
- <a href="https://github.com/99designs/gqlgen/commit/0354649c0309af6acfe089d12d103060d55a5805"><tt>0354649c</tt></a> Remove archived dependency appdash (<a href="https://github.com/99designs/gqlgen/pull/2866">#2866</a>)
- <a href="https://github.com/99designs/gqlgen/commit/0d43599cdab22912d4ddd061c3b3ffd5d8da3845"><tt>0d43599c</tt></a> Update examples go.mod with appdash replacements (<a href="https://github.com/99designs/gqlgen/pull/2863">#2863</a>)
- <a href="https://github.com/99designs/gqlgen/commit/7dd971c871c0b0159ad26c9bf3095a8ba3780402"><tt>7dd971c8</tt></a> Use defer wg.Done() in FieldSet Dispatch (<a href="https://github.com/99designs/gqlgen/pull/2861">#2861</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/24ea195cebea095035caf4d23af7f3d75fd0a041"><tt>24ea195c</tt></a> vikstrous/dataloadgen replaces recommended dataloader package in example docs (<a href="https://github.com/99designs/gqlgen/pull/2770">#2770</a>)</summary>
* update example for dataloadgen
* improved example with link to example repo
* undo unnecessary changes
* fix wrong signature
* fix creation of loader
* Update docs/content/reference/dataloaders.md
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/42f6e39d48e3a301bf39cd4e8fd180250bc25f2c"><tt>42f6e39d</tt></a> Allow fields that return root level definitions (<a href="https://github.com/99designs/gqlgen/pull/2858">#2858</a>)</summary>
* generate structs for root level definitions to support fields that return Query, Mutation or Subscription
* removed unnecessary comment
* re-ran go generate
---------
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/682a58dd6af5fda53509fbf4cfa45d23b5bb1c86"><tt>682a58dd</tt></a> Add go generate for examples so contributors never forget (<a href="https://github.com/99designs/gqlgen/pull/2859">#2859</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/e080a96de178520fcfaf5a8d68836981ec4df9a9"><tt>e080a96d</tt></a> Modify to prevent unreachable code from occurring (<a href="https://github.com/99designs/gqlgen/pull/2846">#2846</a>)</summary>
* fix: 型の数でソートする処理を追加
* 戻し
* fix: case文の最初にスーパークラスが来ないようにする
* testdata追加
* fix: Added sorting by number of types.
* fix: Prevent superclass from appearing at the beginning of case statement
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/68744ad2a1e9d5869ab6a00b49814c6ae9583186"><tt>68744ad2</tt></a> Bump changelog
- <a href="https://github.com/99designs/gqlgen/commit/e4cf21d24518deb99af6d4c0ea86de11d6889349"><tt>e4cf21d2</tt></a> v0.17.41 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.41"></a>
## [v0.17.41](https://github.com/99designs/gqlgen/compare/v0.17.40...v0.17.41) - 2023-12-03
- <a href="https://github.com/99designs/gqlgen/commit/fe60938c55308b1cd5562556cdb976771cfcc6cc"><tt>fe60938c</tt></a> release v0.17.41
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/5e98a16a3a5a5678f1b6481275d81f52f9462f90"><tt>5e98a16a</tt></a> fix fieldset.New bug when prefix slice has len < cap (<a href="https://github.com/99designs/gqlgen/pull/2851">#2851</a>)</summary>
* fix fieldset.New bug when prefix slice has len < cap
* ignore gocritic warning
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/bd9657f3e50b7b9642c05039a5364ce2262faaf4"><tt>bd9657f3</tt></a> Improve ResolverImplementer.Implment (<a href="https://github.com/99designs/gqlgen/pull/2850">#2850</a>)</summary>
* improve resolver implement render
* add error when multiple implementors
* add initial test
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/cb3c1c890e5a255776df9577c80b0c15218cf254"><tt>cb3c1c89</tt></a> Updated apollo sandbox (<a href="https://github.com/99designs/gqlgen/pull/2849">#2849</a>)</summary>
Added all supported options to new window.EmbeddedSandbox object
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/eb5cea7206767bda3582040fd9e4c98174aaa6b1"><tt>eb5cea72</tt></a> Small template fix to save space in the generated file (<a href="https://github.com/99designs/gqlgen/pull/2841">#2841</a>)</summary>
* Small template fix to save space in the generated file
* Re-generate
---------
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/c0ca5091a10417c34192da4d3c064a0fed2a7fdb"><tt>c0ca5091</tt></a> Omittable can now be serialized as json (<a href="https://github.com/99designs/gqlgen/pull/2839">#2839</a>)
- <a href="https://github.com/99designs/gqlgen/commit/dcb7619111642cc82a21b8e80ce1300213af1368"><tt>dcb76191</tt></a> fix: sample program indentation (<a href="https://github.com/99designs/gqlgen/pull/2840">#2840</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/132ec1ce579e9ce3dc772afebf9703c1403d588e"><tt>132ec1ce</tt></a> Updated GraphiQL 3.0.1 => 3.0.6 (<a href="https://github.com/99designs/gqlgen/pull/2837">#2837</a>)</summary>
* Updated GraphiQL 3.0.1 => 3.0.6
* Added unit tests to cover integrity of playgrounds
* Updated vulnerable dependency
* Close response body
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/917407005eb4198aa43875984fb77caeaa7fca36"><tt>91740700</tt></a> v0.17.40 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.40"></a>
## [v0.17.40](https://github.com/99designs/gqlgen/compare/v0.17.39...v0.17.40) - 2023-10-24
- <a href="https://github.com/99designs/gqlgen/commit/c5ad14bf6c4203cefee7888e35e1f07e3a46e77a"><tt>c5ad14bf</tt></a> release v0.17.40
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/74e918f9271dbd045a0425e1f6ddb4804e30404b"><tt>74e918f9</tt></a> Map based input types fields are now coerced to the right type (<a href="https://github.com/99designs/gqlgen/pull/2830">#2830</a>)</summary>
* Input maps now unmarshals and checks nested fields
* Added unit tests
* Tested required fields in input maps
* Docs updated with disclaimer
* Added test for nested inputs
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/1e5fa72a260d2313cf2d2f3315c5f885aa209acd"><tt>1e5fa72a</tt></a> Bump [@babel](https://github.com/babel)/traverse from 7.22.5 to 7.23.2 in /integration (<a href="https://github.com/99designs/gqlgen/pull/2831">#2831</a>)</summary>
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)
---
updated-dependencies:
dependency-type: indirect
...
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/e5c17d63948dd20879e4a9db68863689842ab49a"><tt>e5c17d63</tt></a> resolver: fix case-insensitive file name collision (<a href="https://github.com/99designs/gqlgen/pull/2829">#2829</a>)</summary>
for compatibility with Windows/Mac OS X, Go does not allow files with
identical case-insensitive names.
this commit changes the key of the 'files' map to use the lower case
file name, keeping the original file name as a property on the File
object.
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/9c5fc3024574d0b954962d2e6453fa7489ba6327"><tt>9c5fc302</tt></a> v0.17.39 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.39"></a>
## [v0.17.39](https://github.com/99designs/gqlgen/compare/v0.17.38...v0.17.39) - 2023-10-05
- <a href="https://github.com/99designs/gqlgen/commit/eed94e8caddb251cc6c180062b26708b8b06dd35"><tt>eed94e8c</tt></a> release v0.17.39
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/a863d6451eb1bb54673a86de79004f2182501ac1"><tt>a863d645</tt></a> Add [@interfaceObject](https://github.com/interfaceObject) and [@composeDirective](https://github.com/composeDirective) at Federation 2 directive lists. (<a href="https://github.com/99designs/gqlgen/pull/2821">#2821</a>)</summary>
This commit just adding those directives into the list.
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/3ff523ba5f4564432f7c8c0631ccc3d25754a407"><tt>3ff523ba</tt></a> Bump postcss from 8.4.24 to 8.4.31 in /integration (<a href="https://github.com/99designs/gqlgen/pull/2819">#2819</a>)</summary>
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.24 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.24...8.4.31)
---
updated-dependencies:
- dependency-name: postcss
dependency-type: indirect
...
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/5925864212bb286e22a3027c3900aa99874cc7ad"><tt>59258642</tt></a> Adding duration scalar conforming to ISO8601 standard (<a href="https://github.com/99designs/gqlgen/pull/2800">#2800</a>)</summary>
* Adding duration scalar
* simple Duration scalar docs
* using MarshalString, to add proper double quotes
* adding deps and running go mod tidy on _examples
* Re-organize imports
* Fix test now that imports are sorted
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/37f8e4ebb45d5a7edac5e8f6a241d842b4b9cfb1"><tt>37f8e4eb</tt></a> Add ability to not fail when pong is not received. (<a href="https://github.com/99designs/gqlgen/pull/2815">#2815</a>)</summary>
I also changed how the read deadline set works a little, the reason for
this is that the protocol allows for pong to be sent without a ping.
So setting a read deadline on receiving pong isn't great. Instead we
should always set the read deadline on sending ping. Though to do this
we need to know whether we have received a pong or not. Because if we
set the read deadline when the previous ping still hasn't received the
pong. Then it will never hit the deadline.
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/89ac736ffe346e9ee95b12b4db467d1a802fe0ac"><tt>89ac736f</tt></a> Store parsed Schema on ExecutableSchema rather than use global variable (<a href="https://github.com/99designs/gqlgen/pull/2811">#2811</a>)</summary>
* codegen: executableSchema schema configurable
* feat
* feat
* codegen: add schema property on Config and executableSchema
* fix: fmt
* regenerate
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/919aded6be6cd722c202214e7a8183ed56762501"><tt>919aded6</tt></a> Add a pong only keep alive for the new protcol (<a href="https://github.com/99designs/gqlgen/pull/2814">#2814</a>)</summary>
The protocol allows for this and this eliminates the potential for
over-agressive triggers of the read deadline set for using the ping/pong
flow.
https://github.com/enisdenjo/graphql-ws/blob/50d5a512d0d7252d41c079e6716b884a191b1ddc/PROTOCOL.md#pong
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/001c296a39ee981d415f5079ce0a8827e5d65874"><tt>001c296a</tt></a> Update auto-generated files with latest results. (<a href="https://github.com/99designs/gqlgen/pull/2813">#2813</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/f6fa3aae778926668b0787605c0eab125a800c1e"><tt>f6fa3aae</tt></a> Consider go type name when autobinding (<a href="https://github.com/99designs/gqlgen/pull/2812">#2812</a>)</summary>
Currently, generated schema type names are normalized, for instance - SomeTYPE in the schema will be generated as SomeType in the model.
When autobinding, however, we only consider the schema type name when searching for it in the relevant package(s),
thus type names that differ post normalizations aren't auto-bound properly and are instead re-generated.
This commit suggests a fix where we'd try to autobind for both the schema type name (first, to maintain back compat), or the go type name if the former isn't found.
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/4e8d8c70ce216824e506ad3d77b005f56c3ed144"><tt>4e8d8c70</tt></a> Feature: Support Apollo Federation Auth Directives (<a href="https://github.com/99designs/gqlgen/pull/2809">#2809</a>)</summary>
* local version working
* gofmt
* tabs
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/66709d89f0fd45eca823382d3a95020ef5c7485c"><tt>66709d89</tt></a> feat: update getting-started CreateTodo mutationResolver (<a href="https://github.com/99designs/gqlgen/pull/2810">#2810</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/2c9f9c5f828bdda8aa03c4be7adf1d0c9abaa8f6"><tt>2c9f9c5f</tt></a> fix: CodeGen for omit_slice_element_pointers and GetMany Entity Resolvers (<a href="https://github.com/99designs/gqlgen/pull/2802">#2802</a>)</summary>
* remove ! from reps definition
* adding tests
* fixing tests
* adding documentation
* addressing lint
* commit after go gonerate
* gofmt
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/3e2393f3afe1f3f530c34f4dbbece47bde6e736b"><tt>3e2393f3</tt></a> add close flag into wsConnection to avoid duplicate calls of CloseFunc (<a href="https://github.com/99designs/gqlgen/pull/2803">#2803</a>)</summary>
* add close flag into wsConnection to avoid duplicate calls of CloseFunc
* add test
* Fix linter error
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/af4d3943308b7a84464771e5ed6b578ba40ee0c3"><tt>af4d3943</tt></a> Allow WebsocketInitFunc to add payload to Ack (<a href="https://github.com/99designs/gqlgen/pull/4">#4</a>) (<a href="https://github.com/99designs/gqlgen/pull/2791">#2791</a>)</summary>
* Allow WebsocketInitFunc to add payload to Ack
The connection ACK message in the protocol for both
graphql-ws and graphql-transport-ws allows for a payload in the
connection ack message.
We really wanted to use this to establish better telemetry in our use of
websockets in graphql.
* Fix lint error in test
* Switch argument ordering.
---------
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/f3b860336bfcc7307713420e703ee5badec029a4"><tt>f3b86033</tt></a> v0.17.38 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.38"></a>
## [v0.17.38](https://github.com/99designs/gqlgen/compare/v0.17.37...v0.17.38) - 2023-09-19
- <a href="https://github.com/99designs/gqlgen/commit/d9077fac9c2320b28744fb43ab1d00c4d50f0af8"><tt>d9077fac</tt></a> release v0.17.38
- <a href="https://github.com/99designs/gqlgen/commit/5f452ce2dbfc4171fc021c10de11b435a60c8955"><tt>5f452ce2</tt></a> Update gqlparser to 2.5.10 (<a href="https://github.com/99designs/gqlgen/pull/2798">#2798</a>)
- <a href="https://github.com/99designs/gqlgen/commit/c89860bdd01827ad0f7a3f2020935e2ae115ecb7"><tt>c89860bd</tt></a> refactor: return `null` instead of zero value uuid (<a href="https://github.com/99designs/gqlgen/pull/2794">#2794</a>)
- <a href="https://github.com/99designs/gqlgen/commit/625ca2e58aba47cd540f0fb47e048a743a896906"><tt>625ca2e5</tt></a> Make it possible to pass UI headers (<a href="https://github.com/99designs/gqlgen/pull/2793">#2793</a>)
- <a href="https://github.com/99designs/gqlgen/commit/fceb33111bef2bbbf14468d78888098ac8968351"><tt>fceb3311</tt></a> Fix rand int docs link in Getting Started (<a href="https://github.com/99designs/gqlgen/pull/2789">#2789</a>)
- <a href="https://github.com/99designs/gqlgen/commit/f01d580501ddca4cf20b6fbf7f937be8509466bf"><tt>f01d5805</tt></a> Add new changelog (<a href="https://github.com/99designs/gqlgen/pull/2787">#2787</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/9930e5740e0670a69bfd4397d87222b49cf7f0e6"><tt>9930e574</tt></a> Ability to use forceGenerate and extraFields together (<a href="https://github.com/99designs/gqlgen/pull/2788">#2788</a>)</summary>
* Ability to user forceGenerate and extraFields together
* Some docs for forceGenerate added
---------
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/f90ac05e42fcc97addda2d32e3cc16565f55ed98"><tt>f90ac05e</tt></a> v0.17.37 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.37"></a>
## [v0.17.37](https://github.com/99designs/gqlgen/compare/v0.17.36...v0.17.37) - 2023-09-08
- <a href="https://github.com/99designs/gqlgen/commit/ccae370e96cbca6ce5deaabf28a6d57e3b181b3b"><tt>ccae370e</tt></a> release v0.17.37
- <a href="https://github.com/99designs/gqlgen/commit/6505f8be0d99593376d6c9a0dea00af5e3c018ea"><tt>6505f8be</tt></a> Update gqlparser (<a href="https://github.com/99designs/gqlgen/pull/2785">#2785</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/153ec470d993a39c1656fc45432740f1d3dd10ea"><tt>153ec470</tt></a> add uuid type (<a href="https://github.com/99designs/gqlgen/pull/2751">#2751</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2749"> #2749</a>)</summary>
* add uuid type
* add uuid example
* add uuid scalar doc
* strconv.Quote
* Apply suggestions from code review
* fix
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/fa4711801c59d27db887a50e1f8393006268194e"><tt>fa471180</tt></a> ForceGenerate parameter to [@goModel](https://github.com/goModel) added. (<a href="https://github.com/99designs/gqlgen/pull/2780">#2780</a>)</summary>
* forceGenerate to docs added
---------
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/11bb9b1890fd9e5909f06feba7b2985bf09785c9"><tt>11bb9b18</tt></a> codegen: add support for `go_build_tags` option in gqlgen.yaml (<a href="https://github.com/99designs/gqlgen/pull/2784">#2784</a>)</summary>
* codegen: support go_build_tags option in gqlgen.yaml
* chore: added test
* docs/content: update config example
* chore: more comment
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/bee47dcf1f3edb95e7a77e612ea27d85a417b12c"><tt>bee47dcf</tt></a> fix flaky test TestSubscriptions (<a href="https://github.com/99designs/gqlgen/pull/2779">#2779</a>)</summary>
* fix flaky test TestSubscriptions
* update other copy of the test
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/a41f4daad66cb0c102bfa09d7bff5a057d378197"><tt>a41f4daa</tt></a> docs: short-lived loader (<a href="https://github.com/99designs/gqlgen/pull/2778">#2778</a>)
- <a href="https://github.com/99designs/gqlgen/commit/cc4e0ba28375e0ec39c586485fa138c28e5bfcba"><tt>cc4e0ba2</tt></a> ensure HasOperationContext checks for nil (<a href="https://github.com/99designs/gqlgen/pull/2776">#2776</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/a1ca220477092398efc8e587dd653f5cb967d0e9"><tt>a1ca2204</tt></a> fix typo in TESTING.md server path (<a href="https://github.com/99designs/gqlgen/pull/2774">#2774</a>)</summary>
following TESTING.md instructions, I got an error:
"stat ./server/server.go: no such file or directory"
server.go path is: integration/server/cmd/integration/server.go
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/1cde8c3fab65847a6e8d7f423215b786b47c19df"><tt>1cde8c3f</tt></a> return internal types in schema introspection (<a href="https://github.com/99designs/gqlgen/pull/2773">#2773</a>)</summary>
according to graphql spec:
```
types: return the set of all named types contained within this schema.
Any named type which can be found through a field of any introspection type must be included in this set.
```
source: https://github.com/graphql/graphql-spec/blob/main/spec/Section%204%20--%20Introspection.md#the-__schema-type
some clients libs (like HotChocolate for C#) depends on this behavior.
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/065aea3efa27f0662eb2f62ef2dd131e153009ba"><tt>065aea3e</tt></a> Fix gqlgen truncates tag value with colon (<a href="https://github.com/99designs/gqlgen/pull/2759">#2759</a>)
- <a href="https://github.com/99designs/gqlgen/commit/d6270e4f4fd951c71ee2a2b997d7098b04e07976"><tt>d6270e4f</tt></a> Update subsciptions documentation to correctly close channel (<a href="https://github.com/99designs/gqlgen/pull/2753">#2753</a>)
- <a href="https://github.com/99designs/gqlgen/commit/2d8673a691deffe6ddd1f4d5f013a52dc91aef91"><tt>2d8673a6</tt></a> Add Model references to Interface (<a href="https://github.com/99designs/gqlgen/pull/2738">#2738</a>)
- <a href="https://github.com/99designs/gqlgen/commit/790d7a7571865b8b8324557e1a565f40c23217c8"><tt>790d7a75</tt></a> Allow GraphiQL headers to be set when creating the playground handler (<a href="https://github.com/99designs/gqlgen/pull/2740">#2740</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2739"> #2739</a>)
- <a href="https://github.com/99designs/gqlgen/commit/0eb95dc4315fc5f74431df03e008283cf9ec0c35"><tt>0eb95dc4</tt></a> v0.17.36 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.36"></a>
## [v0.17.36](https://github.com/99designs/gqlgen/compare/v0.17.35...v0.17.36) - 2023-07-27
- <a href="https://github.com/99designs/gqlgen/commit/bd6cfd3108818cd060a06491052c36fb6f5e4be3"><tt>bd6cfd31</tt></a> release v0.17.36
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/60ec0d86847c97cd45c6e431d49b7ccf50efeeb9"><tt>60ec0d86</tt></a> Fix plugin template resolution (<a href="https://github.com/99designs/gqlgen/pull/2733">#2733</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2262"> #2262</a>)</summary>
- According to the documentation comment for [templates.Options], if the
`Template` and `TemplateFS` fields are empty, it `Render` should find
the `.gotpl` files from the calling plugin. However, it looks like
helper function. This results in broken behavior in consumers such as
[infiotinc/gqlgenc](https://github.com/infiotinc/gqlgenc) when they
use the latest version of `gqlgen` as instead of finding the template
from the plugin, the test template from this package is used which
outputs only: `this is my test package`.
- The cause for this is that `runtime.Caller` was still only skipping
one stack level which means that it was finding the `Render` function
instead of its caller.
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/76d444c15fc1d98538648a229f41ed7b4573bad7"><tt>76d444c1</tt></a> Make models configurable via template (<a href="https://github.com/99designs/gqlgen/pull/2730">#2730</a>)
- <a href="https://github.com/99designs/gqlgen/commit/abe3ffde59d6a0d942a9ac2d1b657287f75efa67"><tt>abe3ffde</tt></a> Don't set the package variable for the new Resolver Template (<a href="https://github.com/99designs/gqlgen/pull/2725">#2725</a>)
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/febf9566224b3e3ad0e32903fd46d9005d6a7748"><tt>febf9566</tt></a> Make the resolver implementation configurable via a new template resolver.gotpl (<a href="https://github.com/99designs/gqlgen/pull/2720">#2720</a>)</summary>
* Make an optional resolver.gotpl ResolverTemplate to implement a custom resolver
* Add test
* Add documetation for the new resolver option
* Change the tab to spaces
* remove unecessary test assertion :/
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/bda3026043f187bf00f039d1c3648d5b1a8e4998"><tt>bda30260</tt></a> Fixed Data Loader docs (<a href="https://github.com/99designs/gqlgen/pull/2723">#2723</a>)</summary>
Also updated to v7
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/16c9eb64ed04218e927a74199a57f951bfb452b1"><tt>16c9eb64</tt></a> Fix docs (<a href="https://github.com/99designs/gqlgen/pull/2722">#2722</a>)</summary>
* docs: fix variable names in dataloader sample
* fix: request-scoped middleware
</details></dd></dl>
<dl><dd><details><summary><a href="https://github.com/99designs/gqlgen/commit/b233a01bf7eeb9f9ddc590a7dd72058da28d88df"><tt>b233a01b</tt></a> docs: update dataloader docs (<a href="https://github.com/99designs/gqlgen/pull/2719">#2719</a>)</summary>
* docs: update example
* docs: update example
* fix: import
</details></dd></dl>
- <a href="https://github.com/99designs/gqlgen/commit/cccc7389c10cdd4ef4eb7b9541dedb0f9cf74a97"><tt>cccc7389</tt></a> Added go mod tidy to quick start guide (<a href="https://github.com/99designs/gqlgen/pull/2718">#2718</a>) (closes <a href="https://github.com/99designs/gqlgen/issues/2717"> #2717</a>, <a href="https://github.com/99designs/gqlgen/issues/2651"> #2651</a>, <a href="https://github.com/99designs/gqlgen/issues/2641"> #2641</a>, <a href="https://github.com/99designs/gqlgen/issues/2614"> #2614</a>, <a href="https://github.com/99designs/gqlgen/issues/2576"> #2576</a>)
- <a href="https://github.com/99designs/gqlgen/commit/9adc7b81ac27c157e597d369e4499353f0f6b0b0"><tt>9adc7b81</tt></a> Update gqlparser to v2.5.8 (<a href="https://github.com/99designs/gqlgen/pull/2716">#2716</a>)
- <a href="https://github.com/99designs/gqlgen/commit/b442fbf457498c8ad318578b570c5a6b64cab452"><tt>b442fbf4</tt></a> Post v0.17.35 changelog update
- <a href="https://github.com/99designs/gqlgen/commit/57c121994b1f843f62f99ada71a707e4f263a9df"><tt>57c12199</tt></a> v0.17.35 postrelease bump
<!-- end of Commits -->
<!-- end of Else -->
<!-- end of If NoteGroups -->
<a name="v0.17.35"></a>
## [v0.17.35](https://github.com/99designs/gqlgen/compare/v0.17.34...v0.17.35) - 2023-07-15
- <a href="https://github.com/99designs/gqlgen/commit/05006bf1dcb9451b0960d07fd68370145c2697b4"><tt>05006bf1</tt></a> release v0.17.35

View File

@ -86,7 +86,7 @@ models:
- #### Using Explicit Resolvers
If you want to Keep using the generated model, mark the field as requiring a resolver explicitly in `gqlgen.yml` like this:
If you want to keep using the generated model, mark the field as requiring a resolver explicitly in `gqlgen.yml` like this:
```yaml
# gqlgen.yml

View File

@ -25,7 +25,7 @@ Setting up the integration environment is a little tricky:
```bash
cd integration
go generate ./...
go run ./server/server.go
go run ./server/cmd/integration/server.go
```
in another terminal
```bash

View File

@ -27,14 +27,18 @@ func Generate(cfg *config.Config, option ...Option) error {
if cfg.Federation.IsDefined() {
if cfg.Federation.Version == 0 { // default to using the user's choice of version, but if unset, try to sort out which federation version to use
urlRegex := regexp.MustCompile(`(?s)@link.*\(.*url:.*?"(.*?)"[^)]+\)`) // regex to grab the url of a link directive, should it exist
versionRegex := regexp.MustCompile(`v(\d+).(\d+)$`) // regex to grab the version number from a url
// check the sources, and if one is marked as federation v2, we mark the entirety to be generated using that format
for _, v := range cfg.Sources {
cfg.Federation.Version = 1
urlString := urlRegex.FindStringSubmatch(v.Input)
if urlString != nil && urlString[1] == "https://specs.apollo.dev/federation/v2.0" {
cfg.Federation.Version = 2
break
// e.g. urlString[1] == "https://specs.apollo.dev/federation/v2.7"
if urlString != nil {
matches := versionRegex.FindStringSubmatch(urlString[1])
if matches[1] == "2" {
cfg.Federation.Version = 2
break
}
}
}
}

View File

@ -5,9 +5,10 @@ import (
"go/types"
"strings"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
)
type ArgSet struct {

View File

@ -5,12 +5,12 @@ import (
"fmt"
"go/token"
"go/types"
"strings"
"github.com/vektah/gqlparser/v2/ast"
"golang.org/x/tools/go/packages"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/internal/code"
"github.com/vektah/gqlparser/v2/ast"
)
var ErrTypeNotFound = errors.New("unable to find type")
@ -203,6 +203,7 @@ type TypeReference struct {
IsOmittable bool // Is the type wrapped with Omittable
IsContext bool // Is the Marshaler/Unmarshaller the context version; applies to either the method or interface variety.
PointersInUmarshalInput bool // Inverse values and pointers in return.
IsRoot bool // Is the type a root level definition such as Query, Mutation or Subscription
}
func (ref *TypeReference) Elem() *TypeReference {
@ -274,6 +275,10 @@ func (ref *TypeReference) IsScalar() bool {
return ref.Definition.Kind == ast.Scalar
}
func (ref *TypeReference) IsMap() bool {
return ref.GO == MapType
}
func (ref *TypeReference) UniquenessKey() string {
nullability := "O"
if ref.GQL.NonNull {
@ -285,7 +290,7 @@ func (ref *TypeReference) UniquenessKey() string {
// Fix for #896
elemNullability = "ᚄ"
}
return nullability + ref.Definition.Name + "2" + TypeIdentifier(ref.GO) + elemNullability
return nullability + ref.Definition.Name + "2" + templates.TypeIdentifier(ref.GO) + elemNullability
}
func (ref *TypeReference) MarshalFunc() string {
@ -391,6 +396,7 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret
Definition: def,
GQL: schemaType,
GO: MapType,
IsRoot: b.cfg.IsRoot(def),
}, nil
}
@ -402,6 +408,7 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret
Definition: def,
GQL: schemaType,
GO: InterfaceType,
IsRoot: b.cfg.IsRoot(def),
}, nil
}
@ -413,6 +420,7 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret
ref := &TypeReference{
Definition: def,
GQL: schemaType,
IsRoot: b.cfg.IsRoot(def),
}
obj, err := b.FindObject(pkgName, typeName)
@ -540,41 +548,3 @@ func basicUnderlying(it types.Type) *types.Basic {
return nil
}
var pkgReplacer = strings.NewReplacer(
"/", "ᚋ",
".", "ᚗ",
"-", "ᚑ",
"~", "א",
)
func TypeIdentifier(t types.Type) string {
res := ""
for {
switch it := t.(type) {
case *types.Pointer:
t.Underlying()
res += "ᚖ"
t = it.Elem()
case *types.Slice:
res += "ᚕ"
t = it.Elem()
case *types.Named:
res += pkgReplacer.Replace(it.Obj().Pkg().Path())
res += "ᚐ"
res += it.Obj().Name()
return res
case *types.Basic:
res += it.Name()
return res
case *types.Map:
res += "map"
return res
case *types.Interface:
res += "interface"
return res
default:
panic(fmt.Errorf("unexpected type %T", it))
}
}
}

View File

@ -3,6 +3,7 @@ package config
import (
"bytes"
"fmt"
"go/types"
"io"
"os"
"path/filepath"
@ -10,10 +11,13 @@ import (
"sort"
"strings"
"github.com/99designs/gqlgen/internal/code"
"github.com/vektah/gqlparser/v2"
"github.com/vektah/gqlparser/v2/ast"
"golang.org/x/tools/go/packages"
"gopkg.in/yaml.v3"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/internal/code"
)
type Config struct {
@ -26,6 +30,7 @@ type Config struct {
Models TypeMap `yaml:"models,omitempty"`
StructTag string `yaml:"struct_tag,omitempty"`
Directives map[string]DirectiveConfig `yaml:"directives,omitempty"`
GoBuildTags StringList `yaml:"go_build_tags,omitempty"`
GoInitialisms GoInitialismsConfig `yaml:"go_initialisms,omitempty"`
OmitSliceElementPointers bool `yaml:"omit_slice_element_pointers,omitempty"`
OmitGetters bool `yaml:"omit_getters,omitempty"`
@ -33,6 +38,8 @@ type Config struct {
OmitComplexity bool `yaml:"omit_complexity,omitempty"`
OmitGQLGenFileNotice bool `yaml:"omit_gqlgen_file_notice,omitempty"`
OmitGQLGenVersionInFileNotice bool `yaml:"omit_gqlgen_version_in_file_notice,omitempty"`
OmitRootModels bool `yaml:"omit_root_models,omitempty"`
OmitResolverFields bool `yaml:"omit_resolver_fields,omitempty"`
StructFieldsAlwaysPointers bool `yaml:"struct_fields_always_pointers,omitempty"`
ReturnPointersInUmarshalInput bool `yaml:"return_pointers_in_unmarshalinput,omitempty"`
ResolversAlwaysReturnPointers bool `yaml:"resolvers_always_return_pointers,omitempty"`
@ -211,7 +218,9 @@ func CompleteConfig(config *Config) error {
func (c *Config) Init() error {
if c.Packages == nil {
c.Packages = &code.Packages{}
c.Packages = code.NewPackages(
code.WithBuildTags(c.GoBuildTags...),
)
}
if c.Schema == nil {
@ -257,6 +266,10 @@ func (c *Config) ReloadAllPackages() {
c.Packages.ReloadAll(c.packageList()...)
}
func (c *Config) IsRoot(def *ast.Definition) bool {
return def == c.Schema.Query || def == c.Schema.Mutation || def == c.Schema.Subscription
}
func (c *Config) injectTypesFromSchema() error {
c.Directives["goModel"] = DirectiveConfig{
SkipRuntime: true,
@ -271,7 +284,7 @@ func (c *Config) injectTypesFromSchema() error {
}
for _, schemaType := range c.Schema.Types {
if schemaType == c.Schema.Query || schemaType == c.Schema.Mutation || schemaType == c.Schema.Subscription {
if c.IsRoot(schemaType) {
continue
}
@ -281,6 +294,7 @@ func (c *Config) injectTypesFromSchema() error {
c.Models.Add(schemaType.Name, mv.(string))
}
}
if ma := bd.Arguments.ForName("models"); ma != nil {
if mvs, err := ma.Value.Value(nil); err == nil {
for _, mv := range mvs.([]interface{}) {
@ -288,6 +302,12 @@ func (c *Config) injectTypesFromSchema() error {
}
}
}
if fg := bd.Arguments.ForName("forceGenerate"); fg != nil {
if mv, err := fg.Value.Value(nil); err == nil {
c.Models.ForceGenerate(schemaType.Name, mv.(bool))
}
}
}
if schemaType.Kind == ast.Object || schemaType.Kind == ast.InputObject {
@ -329,8 +349,9 @@ func (c *Config) injectTypesFromSchema() error {
}
type TypeMapEntry struct {
Model StringList `yaml:"model"`
Fields map[string]TypeMapField `yaml:"fields,omitempty"`
Model StringList `yaml:"model,omitempty"`
ForceGenerate bool `yaml:"forceGenerate,omitempty"`
Fields map[string]TypeMapField `yaml:"fields,omitempty"`
// Key is the Go name of the field.
ExtraFields map[string]ModelExtraField `yaml:"extraFields,omitempty"`
@ -529,6 +550,12 @@ func (tm TypeMap) Add(name string, goType string) {
tm[name] = modelCfg
}
func (tm TypeMap) ForceGenerate(name string, forceGenerate bool) {
modelCfg := tm[name]
modelCfg.ForceGenerate = forceGenerate
tm[name] = modelCfg
}
type DirectiveConfig struct {
SkipRuntime bool `yaml:"skip_runtime"`
}
@ -583,7 +610,7 @@ func (c *Config) autobind() error {
ps := c.Packages.LoadAll(c.AutoBind...)
for _, t := range c.Schema.Types {
if c.Models.UserDefined(t.Name) {
if c.Models.UserDefined(t.Name) || c.Models[t.Name].ForceGenerate {
continue
}
@ -591,14 +618,20 @@ func (c *Config) autobind() error {
if p == nil || p.Module == nil {
return fmt.Errorf("unable to load %s - make sure you're using an import path to a package that exists", c.AutoBind[i])
}
if t := p.Types.Scope().Lookup(t.Name); t != nil {
c.Models.Add(t.Name(), t.Pkg().Path()+"."+t.Name())
autobindType := c.lookupAutobindType(p, t)
if autobindType != nil {
c.Models.Add(t.Name, autobindType.Pkg().Path()+"."+autobindType.Name())
break
}
}
}
for i, t := range c.Models {
if t.ForceGenerate {
continue
}
for j, m := range t.Model {
pkg, typename := code.PkgAndType(m)
@ -622,6 +655,17 @@ func (c *Config) autobind() error {
return nil
}
func (c *Config) lookupAutobindType(p *packages.Package, schemaType *ast.Definition) types.Object {
// Try binding to either the original schema type name, or the normalized go type name
for _, lookupName := range []string{schemaType.Name, templates.ToGo(schemaType.Name)} {
if t := p.Types.Scope().Lookup(lookupName); t != nil {
return t
}
}
return nil
}
func (c *Config) injectBuiltins() {
builtins := TypeMap{
"__Directive": {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Directive"}},
@ -671,7 +715,9 @@ func (c *Config) injectBuiltins() {
func (c *Config) LoadSchema() error {
if c.Packages != nil {
c.Packages = &code.Packages{}
c.Packages = code.NewPackages(
code.WithBuildTags(c.GoBuildTags...),
)
}
if err := c.check(); err != nil {

View File

@ -1,62 +1,10 @@
package config
import "strings"
import (
"strings"
// commonInitialisms is a set of common initialisms.
// Only add entries that are highly unlikely to be non-initialisms.
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
var commonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"CSV": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ICMP": true,
"ID": true,
"IP": true,
"JSON": true,
"KVK": true,
"LHS": true,
"PDF": true,
"PGP": true,
"QPS": true,
"QR": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSH": true,
"SVG": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"URI": true,
"URL": true,
"UTF8": true,
"UUID": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
}
// GetInitialisms returns the initialisms to capitalize in Go names. If unchanged, default initialisms will be returned
var GetInitialisms = func() map[string]bool {
return commonInitialisms
}
"github.com/99designs/gqlgen/codegen/templates"
)
// GoInitialismsConfig allows to modify the default behavior of naming Go methods, types and properties
type GoInitialismsConfig struct {
@ -69,7 +17,7 @@ type GoInitialismsConfig struct {
// setInitialisms adjustes GetInitialisms based on its settings.
func (i GoInitialismsConfig) setInitialisms() {
toUse := i.determineGoInitialisms()
GetInitialisms = func() map[string]bool {
templates.GetInitialisms = func() map[string]bool {
return toUse
}
}
@ -82,8 +30,8 @@ func (i GoInitialismsConfig) determineGoInitialisms() (initialismsToUse map[stri
initialismsToUse[strings.ToUpper(initialism)] = true
}
} else {
initialismsToUse = make(map[string]bool, len(commonInitialisms)+len(i.Initialisms))
for initialism, value := range commonInitialisms {
initialismsToUse = make(map[string]bool, len(templates.CommonInitialisms)+len(i.Initialisms))
for initialism, value := range templates.CommonInitialisms {
initialismsToUse[strings.ToUpper(initialism)] = value
}
for _, initialism := range i.Initialisms {

View File

@ -10,10 +10,11 @@ import (
)
type PackageConfig struct {
Filename string `yaml:"filename,omitempty"`
Package string `yaml:"package,omitempty"`
Version int `yaml:"version,omitempty"`
ModelTemplate string `yaml:"model_template,omitempty"`
Filename string `yaml:"filename,omitempty"`
Package string `yaml:"package,omitempty"`
Version int `yaml:"version,omitempty"`
ModelTemplate string `yaml:"model_template,omitempty"`
Options map[string]bool `yaml:"options,omitempty"`
}
func (c *PackageConfig) ImportPath() string {

View File

@ -5,8 +5,9 @@ import (
"strconv"
"strings"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/templates"
)
type DirectiveList map[string]*Directive

View File

@ -10,11 +10,12 @@ import (
"strconv"
"strings"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
)
type Field struct {
@ -152,6 +153,11 @@ func (b *builder) bindField(obj *Object, f *Field) (errret error) {
switch target := target.(type) {
case nil:
// Skips creating a resolver for any root types
if b.Config.IsRoot(b.Schema.Types[f.Type.Name()]) {
return nil
}
objPos := b.Binder.TypePosition(obj.Type)
return fmt.Errorf(
"%s:%d adding resolver method for %s.%s, nothing matched",

View File

@ -16,49 +16,59 @@ func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Contex
ret = {{ $null }}
}
}()
{{- if $.AllDirectives.LocationDirectives "FIELD" }}
resTmp := ec._fieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
})
{{ else }}
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
})
if err != nil {
ec.Error(ctx, err)
return {{ $null }}
}
{{- end }}
if resTmp == nil {
{{- if $field.TypeReference.GQL.NonNull }}
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
{{- end }}
return {{ $null }}
}
{{- if $object.Stream }}
return func(ctx context.Context) graphql.Marshaler {
select {
case res, ok := <-resTmp.(<-chan {{$field.TypeReference.GO | ref}}):
if !ok {
return nil
}
return graphql.WriterFunc(func(w io.Writer) {
w.Write([]byte{'{'})
graphql.MarshalString(field.Alias).MarshalGQL(w)
w.Write([]byte{':'})
ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res).MarshalGQL(w)
w.Write([]byte{'}'})
})
case <-ctx.Done():
return nil
}
}
{{- else }}
res := resTmp.({{$field.TypeReference.GO | ref}})
{{- if $field.TypeReference.IsRoot }}
{{- if $field.TypeReference.IsPtr }}
res := &{{ $field.TypeReference.Elem.GO | ref }}{}
{{- else }}
res := {{ $field.TypeReference.GO | ref }}{}
{{- end }}
fc.Result = res
return ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res)
{{- else}}
{{- if $.AllDirectives.LocationDirectives "FIELD" }}
resTmp := ec._fieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
})
{{ else }}
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
{{ template "field" $field }}
})
if err != nil {
ec.Error(ctx, err)
return {{ $null }}
}
{{- end }}
if resTmp == nil {
{{- if $field.TypeReference.GQL.NonNull }}
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
{{- end }}
return {{ $null }}
}
{{- if $object.Stream }}
return func(ctx context.Context) graphql.Marshaler {
select {
case res, ok := <-resTmp.(<-chan {{$field.TypeReference.GO | ref}}):
if !ok {
return nil
}
return graphql.WriterFunc(func(w io.Writer) {
w.Write([]byte{'{'})
graphql.MarshalString(field.Alias).MarshalGQL(w)
w.Write([]byte{':'})
ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res).MarshalGQL(w)
w.Write([]byte{'}'})
})
case <-ctx.Done():
return nil
}
}
{{- else }}
res := resTmp.({{$field.TypeReference.GO | ref}})
fc.Result = res
return ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res)
{{- end }}
{{- end }}
}

View File

@ -9,9 +9,10 @@ import (
"runtime"
"strings"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
)
//go:embed *.gotpl

View File

@ -18,6 +18,7 @@
// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.
func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
return &executableSchema{
schema: cfg.Schema,
resolvers: cfg.Resolvers,
directives: cfg.Directives,
complexity: cfg.Complexity,
@ -25,6 +26,7 @@
}
type Config struct {
Schema *ast.Schema
Resolvers ResolverRoot
Directives DirectiveRoot
Complexity ComplexityRoot
@ -93,12 +95,16 @@
{{ if eq .Config.Exec.Layout "single-file" }}
type executableSchema struct {
schema *ast.Schema
resolvers ResolverRoot
directives DirectiveRoot
complexity ComplexityRoot
}
func (e *executableSchema) Schema() *ast.Schema {
if e.schema != nil {
return e.schema
}
return parsedSchema
}
@ -268,14 +274,14 @@
if ec.DisableIntrospection {
return nil, errors.New("introspection disabled")
}
return introspection.WrapSchema(parsedSchema), nil
return introspection.WrapSchema(ec.Schema()), nil
}
func (ec *executionContext) introspectType(name string) (*introspection.Type, error) {
if ec.DisableIntrospection {
return nil, errors.New("introspection disabled")
}
return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil
return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil
}
{{if .HasEmbeddableSources }}

View File

@ -5,7 +5,11 @@
{{- $it = "&it" }}
{{- end }}
func (ec *executionContext) unmarshalInput{{ .Name }}(ctx context.Context, obj interface{}) ({{ if .PointersInUmarshalInput }}*{{ end }}{{.Type | ref}}, error) {
var it {{.Type | ref}}
{{- if $input.IsMap }}
it := make(map[string]interface{}, len(obj.(map[string]interface{})))
{{- else }}
var it {{.Type | ref}}
{{- end }}
asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) {
asMap[k] = v
@ -27,8 +31,10 @@
switch k {
{{- range $field := .Fields }}
case {{$field.Name|quote}}:
var err error
{{- $lhs := (printf "it.%s" $field.GoFieldName) }}
{{- if $input.IsMap }}
{{- $lhs = (printf "it[%q]" $field.Name) }}
{{- end }}
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField({{$field.Name|quote}}))
{{- if $field.ImplDirectives }}
directive0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $field.TypeReference.UnmarshalFunc }}(ctx, v) }
@ -44,18 +50,18 @@
}
{{- else }}
{{- if $field.TypeReference.IsOmittable }}
it.{{$field.GoFieldName}} = graphql.OmittableOf(data)
{{ $lhs }} = graphql.OmittableOf(data)
{{- else }}
it.{{$field.GoFieldName}} = data
{{ $lhs }} = data
{{- end }}
{{- end }}
{{- if $field.TypeReference.IsNilable }}
{{- if not $field.IsResolver }}
} else if tmp == nil {
{{- if $field.TypeReference.IsOmittable }}
it.{{$field.GoFieldName}} = graphql.OmittableOf[{{ $field.TypeReference.GO | ref }}](nil)
{{ $lhs }} = graphql.OmittableOf[{{ $field.TypeReference.GO | ref }}](nil)
{{- else }}
it.{{$field.GoFieldName}} = nil
{{ $lhs }} = nil
{{- end }}
{{- end }}
{{- end }}
@ -78,9 +84,9 @@
return {{$it}}, err
}
{{- if $field.TypeReference.IsOmittable }}
it.{{$field.GoFieldName}} = graphql.OmittableOf(data)
{{ $lhs }} = graphql.OmittableOf(data)
{{- else }}
it.{{$field.GoFieldName}} = data
{{ $lhs }} = data
{{- end }}
{{- end }}
{{- end }}

View File

@ -3,6 +3,7 @@ package codegen
import (
"fmt"
"go/types"
"sort"
"github.com/vektah/gqlparser/v2/ast"
@ -40,7 +41,13 @@ func (b *builder) buildInterface(typ *ast.Definition) (*Interface, error) {
return nil, fmt.Errorf("%s is not an interface", i.Type)
}
for _, implementor := range b.Schema.GetPossibleTypes(typ) {
// Sort so that more specific types are evaluated first.
implementors := b.Schema.GetPossibleTypes(typ)
sort.Slice(implementors, func(i, j int) bool {
return len(implementors[i].Interfaces) > len(implementors[j].Interfaces)
})
for _, implementor := range implementors {
obj, err := b.Binder.DefaultUserObject(implementor.Name)
if err != nil {
return nil, fmt.Errorf("%s has no backing go type", implementor.Name)

View File

@ -7,10 +7,11 @@ import (
"strings"
"unicode"
"github.com/99designs/gqlgen/codegen/config"
"github.com/vektah/gqlparser/v2/ast"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/99designs/gqlgen/codegen/config"
)
type GoFieldType int
@ -44,7 +45,7 @@ func (b *builder) buildObject(typ *ast.Definition) (*Object, error) {
caser := cases.Title(language.English, cases.NoLower)
obj := &Object{
Definition: typ,
Root: b.Schema.Query == typ || b.Schema.Mutation == typ || b.Schema.Subscription == typ,
Root: b.Config.IsRoot(typ),
DisableConcurrency: typ == b.Schema.Mutation,
Stream: typ == b.Schema.Subscription,
Directives: dirs,
@ -112,8 +113,8 @@ func (o *Object) HasResolvers() bool {
}
func (o *Object) HasUnmarshal() bool {
if o.Type == config.MapType {
return true
if o.IsMap() {
return false
}
for i := 0; i < o.Type.(*types.Named).NumMethods(); i++ {
if o.Type.(*types.Named).Method(i).Name() == "UnmarshalGQL" {
@ -149,6 +150,10 @@ func (o *Object) IsReserved() bool {
return strings.HasPrefix(o.Definition.Name, "__")
}
func (o *Object) IsMap() bool {
return o.Type == config.MapType
}
func (o *Object) Description() string {
return o.Definition.Description
}

View File

@ -17,6 +17,7 @@
// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.
func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
return &executableSchema{
schema: cfg.Schema,
resolvers: cfg.Resolvers,
directives: cfg.Directives,
complexity: cfg.Complexity,
@ -24,6 +25,7 @@ func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
}
type Config struct {
Schema *ast.Schema
Resolvers ResolverRoot
Directives DirectiveRoot
Complexity ComplexityRoot
@ -66,12 +68,16 @@ type ComplexityRoot struct {
}
type executableSchema struct {
schema *ast.Schema
resolvers ResolverRoot
directives DirectiveRoot
complexity ComplexityRoot
}
func (e *executableSchema) Schema() *ast.Schema {
if e.schema != nil {
return e.schema
}
return parsedSchema
}
@ -241,14 +247,14 @@ func (ec *executionContext) introspectSchema() (*introspection.Schema, error) {
if ec.DisableIntrospection {
return nil, errors.New("introspection disabled")
}
return introspection.WrapSchema(parsedSchema), nil
return introspection.WrapSchema(ec.Schema()), nil
}
func (ec *executionContext) introspectType(name string) (*introspection.Type, error) {
if ec.DisableIntrospection {
return nil, errors.New("introspection disabled")
}
return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil
return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil
}

View File

@ -17,7 +17,6 @@ import (
"text/template"
"unicode"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/internal/code"
"github.com/99designs/gqlgen/internal/imports"
)
@ -202,7 +201,7 @@ func Funcs() template.FuncMap {
"rawQuote": rawQuote,
"dump": Dump,
"ref": ref,
"ts": config.TypeIdentifier,
"ts": TypeIdentifier,
"call": Call,
"prefixLines": prefixLines,
"notNil": notNil,
@ -465,7 +464,7 @@ func wordWalker(str string, f func(*wordInfo)) {
}
i++
initialisms := config.GetInitialisms()
initialisms := GetInitialisms()
// [w,i) is a word.
word := string(runes[w:i])
if !eow && initialisms[word] && !unicode.IsLower(runes[i]) {
@ -668,3 +667,97 @@ func write(filename string, b []byte, packages *code.Packages) error {
return nil
}
var pkgReplacer = strings.NewReplacer(
"/", "ᚋ",
".", "ᚗ",
"-", "ᚑ",
"~", "א",
)
func TypeIdentifier(t types.Type) string {
res := ""
for {
switch it := t.(type) {
case *types.Pointer:
t.Underlying()
res += "ᚖ"
t = it.Elem()
case *types.Slice:
res += "ᚕ"
t = it.Elem()
case *types.Named:
res += pkgReplacer.Replace(it.Obj().Pkg().Path())
res += "ᚐ"
res += it.Obj().Name()
return res
case *types.Basic:
res += it.Name()
return res
case *types.Map:
res += "map"
return res
case *types.Interface:
res += "interface"
return res
default:
panic(fmt.Errorf("unexpected type %T", it))
}
}
}
// CommonInitialisms is a set of common initialisms.
// Only add entries that are highly unlikely to be non-initialisms.
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
var CommonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"CSV": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ICMP": true,
"ID": true,
"IP": true,
"JSON": true,
"KVK": true,
"LHS": true,
"PDF": true,
"PGP": true,
"QPS": true,
"QR": true,
"RAM": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSH": true,
"SVG": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"URI": true,
"URL": true,
"UTF8": true,
"UUID": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
}
// GetInitialisms returns the initialisms to capitalize in Go names. If unchanged, default initialisms will be returned
var GetInitialisms = func() map[string]bool {
return CommonInitialisms
}

View File

@ -59,8 +59,6 @@
{{- else}}
return res, graphql.ErrorOnPath(ctx, err)
{{- end }}
{{- else if eq ($type.GO | ref) "map[string]interface{}" }}
return v.(map[string]interface{}), nil
{{- else if $type.IsMarshaler }}
{{- if and $type.IsNilable $type.Elem }}
var res = new({{ $type.Elem.GO | ref }})
@ -75,7 +73,7 @@
return res, graphql.ErrorOnPath(ctx, err)
{{- else }}
res, err := ec.unmarshalInput{{ $type.GQL.Name }}(ctx, v)
{{- if and $type.IsNilable (not $type.PointersInUmarshalInput) }}
{{- if and $type.IsNilable (not $type.IsMap) (not $type.PointersInUmarshalInput) }}
return &res, graphql.ErrorOnPath(ctx, err)
{{- else if and (not $type.IsNilable) $type.PointersInUmarshalInput }}
return *res, graphql.ErrorOnPath(ctx, err)
@ -185,6 +183,13 @@
{{- else }}
return res
{{- end }}
{{- else if $type.IsRoot }}
{{- if eq $type.Definition.Name "Subscription" }}
res := ec._{{$type.Definition.Name}}(ctx, sel)
return res(ctx)
{{- else }}
return ec._{{$type.Definition.Name}}(ctx, sel)
{{- end }}
{{- else }}
return ec._{{$type.Definition.Name}}(ctx, sel, {{ if not $type.IsNilable}}&{{end}} v)
{{- end }}

View File

@ -1,8 +1,9 @@
package complexity
import (
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/graphql"
)
func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars map[string]interface{}) int {

View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
cd ./_examples
go generate ./... || return 0

View File

@ -73,8 +73,8 @@ func WithOperationContext(ctx context.Context, rc *OperationContext) context.Con
//
// Some errors can happen outside of an operation, eg json unmarshal errors.
func HasOperationContext(ctx context.Context) bool {
_, ok := ctx.Value(operationCtx).(*OperationContext)
return ok
val, ok := ctx.Value(operationCtx).(*OperationContext)
return ok && val != nil
}
// This is just a convenient wrapper method for CollectFields

27
vendor/github.com/99designs/gqlgen/graphql/duration.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package graphql
import (
"fmt"
"time"
dur "github.com/sosodev/duration"
)
// UnmarshalDuration returns the duration from a string in ISO8601 format
func UnmarshalDuration(v interface{}) (time.Duration, error) {
input, ok := v.(string)
if !ok {
return 0, fmt.Errorf("input must be a string")
}
d2, err := dur.Parse(input)
if err != nil {
return 0, err
}
return d2.ToTimeDuration(), nil
}
// MarshalDuration returns the duration on ISO8601 format
func MarshalDuration(d time.Duration) Marshaler {
return MarshalString(dur.Format(d))
}

View File

@ -3,12 +3,13 @@ package executor
import (
"context"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/vektah/gqlparser/v2/parser"
"github.com/vektah/gqlparser/v2/validator"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
)
// Executor executes graphql queries against a schema.

View File

@ -46,8 +46,8 @@ func (m *FieldSet) Dispatch(ctx context.Context) {
for _, d := range m.delayed[1:] {
wg.Add(1)
go func(d delayedResult) {
defer wg.Done()
m.Values[d.i] = d.f(ctx)
wg.Done()
}(d)
}

View File

@ -6,12 +6,11 @@ import (
"encoding/hex"
"fmt"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/mitchellh/mapstructure"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
"github.com/mitchellh/mapstructure"
"github.com/99designs/gqlgen/graphql/errcode"
)
const (

View File

@ -4,10 +4,11 @@ import (
"context"
"fmt"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/complexity"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/vektah/gqlparser/v2/gqlerror"
)
const errComplexityLimit = "COMPLEXITY_LIMIT_EXCEEDED"

View File

@ -3,8 +3,9 @@ package extension
import (
"context"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
)
// EnableIntrospection enables clients to reflect all of the types available on the graph.

View File

@ -3,8 +3,9 @@ package lru
import (
"context"
"github.com/99designs/gqlgen/graphql"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/99designs/gqlgen/graphql"
)
type LRU struct {

View File

@ -7,12 +7,13 @@ import (
"net/http"
"time"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/executor"
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/vektah/gqlparser/v2/gqlerror"
)
type (

View File

@ -5,8 +5,9 @@ import (
"fmt"
"net/http"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
)
// SendError sends a best effort error to a raw response writer. It assumes the client can understand the standard

View File

@ -7,10 +7,11 @@ import (
"net/url"
"strings"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
)
// GET implements the GET side of the default HTTP transport

View File

@ -5,8 +5,9 @@ import (
"fmt"
"io"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
)
func writeJson(w io.Writer, response *graphql.Response) {

View File

@ -12,10 +12,11 @@ import (
"sync"
"time"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
"github.com/gorilla/websocket"
"github.com/vektah/gqlparser/v2/gqlerror"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/errcode"
)
type (
@ -26,7 +27,17 @@ type (
ErrorFunc WebsocketErrorFunc
CloseFunc WebsocketCloseFunc
KeepAlivePingInterval time.Duration
PongOnlyInterval time.Duration
PingPongInterval time.Duration
/* If PingPongInterval has a non-0 duration, then when the server sends a ping
* it sets a ReadDeadline of PingPongInterval*2 and if the client doesn't respond
* with pong before that deadline is reached then the connection will die with a
* 1006 error code.
*
* MissingPongOk if true, tells the server to not use a ReadDeadline such that a
* missing/slow pong response from the client doesn't kill the connection.
*/
MissingPongOk bool
didInjectSubprotocols bool
}
@ -38,13 +49,16 @@ type (
active map[string]context.CancelFunc
mu sync.Mutex
keepAliveTicker *time.Ticker
pongOnlyTicker *time.Ticker
pingPongTicker *time.Ticker
receivedPong bool
exec graphql.GraphExecutor
closed bool
initPayload InitPayload
}
WebsocketInitFunc func(ctx context.Context, initPayload InitPayload) (context.Context, error)
WebsocketInitFunc func(ctx context.Context, initPayload InitPayload) (context.Context, *InitPayload, error)
WebsocketErrorFunc func(ctx context.Context, err error)
// Callback called when websocket is closed.
@ -179,8 +193,10 @@ func (c *wsConnection) init() bool {
}
}
var initAckPayload *InitPayload = nil
if c.InitFunc != nil {
ctx, err := c.InitFunc(c.ctx, c.initPayload)
var ctx context.Context
ctx, initAckPayload, err = c.InitFunc(c.ctx, c.initPayload)
if err != nil {
c.sendConnectionError(err.Error())
c.close(websocket.CloseNormalClosure, "terminated")
@ -189,7 +205,15 @@ func (c *wsConnection) init() bool {
c.ctx = ctx
}
c.write(&message{t: connectionAckMessageType})
if initAckPayload != nil {
initJsonAckPayload, err := json.Marshal(*initAckPayload)
if err != nil {
panic(err)
}
c.write(&message{t: connectionAckMessageType, payload: initJsonAckPayload})
} else {
c.write(&message{t: connectionAckMessageType})
}
c.write(&message{t: keepAliveMessageType})
case connectionCloseMessageType:
c.close(websocket.CloseNormalClosure, "terminated")
@ -228,16 +252,28 @@ func (c *wsConnection) run() {
go c.keepAlive(ctx)
}
// If we're running in graphql-transport-ws mode, create a timer that will trigger a
// just a pong message every interval
if c.conn.Subprotocol() == graphqltransportwsSubprotocol && c.PongOnlyInterval != 0 {
c.mu.Lock()
c.pongOnlyTicker = time.NewTicker(c.PongOnlyInterval)
c.mu.Unlock()
go c.keepAlivePongOnly(ctx)
}
// If we're running in graphql-transport-ws mode, create a timer that will
// trigger a ping message every interval
// trigger a ping message every interval and expect a pong!
if c.conn.Subprotocol() == graphqltransportwsSubprotocol && c.PingPongInterval != 0 {
c.mu.Lock()
c.pingPongTicker = time.NewTicker(c.PingPongInterval)
c.mu.Unlock()
// Note: when the connection is closed by this deadline, the client
// will receive an "invalid close code"
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval))
if !c.MissingPongOk {
// Note: when the connection is closed by this deadline, the client
// will receive an "invalid close code"
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval))
}
go c.ping(ctx)
}
@ -272,7 +308,11 @@ func (c *wsConnection) run() {
case pingMessageType:
c.write(&message{t: pongMessageType, payload: m.payload})
case pongMessageType:
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval))
c.mu.Lock()
c.receivedPong = true
c.mu.Unlock()
// Clear ReadTimeout -- 0 time val clears.
c.conn.SetReadDeadline(time.Time{})
default:
c.sendConnectionError("unexpected message %s", m.t)
c.close(websocket.CloseProtocolError, "unexpected message")
@ -281,6 +321,18 @@ func (c *wsConnection) run() {
}
}
func (c *wsConnection) keepAlivePongOnly(ctx context.Context) {
for {
select {
case <-ctx.Done():
c.pongOnlyTicker.Stop()
return
case <-c.pongOnlyTicker.C:
c.write(&message{t: pongMessageType, payload: json.RawMessage{}})
}
}
}
func (c *wsConnection) keepAlive(ctx context.Context) {
for {
select {
@ -301,6 +353,14 @@ func (c *wsConnection) ping(ctx context.Context) {
return
case <-c.pingPongTicker.C:
c.write(&message{t: pingMessageType, payload: json.RawMessage{}})
// The initial deadline for this method is set in run()
// if we have not yet received a pong, don't reset the deadline.
c.mu.Lock()
if !c.MissingPongOk && c.receivedPong {
c.conn.SetReadDeadline(time.Now().UTC().Add(2 * c.PingPongInterval))
}
c.receivedPong = false
c.mu.Unlock()
}
}
}
@ -431,10 +491,15 @@ func (c *wsConnection) sendConnectionError(format string, args ...interface{}) {
func (c *wsConnection) close(closeCode int, message string) {
c.mu.Lock()
if c.closed {
c.mu.Unlock()
return
}
_ = c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, message))
for _, closer := range c.active {
closer()
}
c.closed = true
c.mu.Unlock()
_ = c.conn.Close()

View File

@ -2,7 +2,6 @@ package introspection
import (
"sort"
"strings"
"github.com/vektah/gqlparser/v2/ast"
)
@ -22,9 +21,6 @@ func (s *Schema) Types() []Type {
typeIndex := map[string]Type{}
typeNames := make([]string, 0, len(s.schema.Types))
for _, typ := range s.schema.Types {
if strings.HasPrefix(typ.Name, "__") {
continue
}
typeNames = append(typeNames, typ.Name)
typeIndex[typ.Name] = *WrapTypeFromDef(s.schema, typ)
}

View File

@ -1,5 +1,7 @@
package graphql
import "encoding/json"
// Omittable is a wrapper around a value that also stores whether it is set
// or not.
type Omittable[T any] struct {
@ -7,6 +9,11 @@ type Omittable[T any] struct {
set bool
}
var (
_ json.Marshaler = Omittable[struct{}]{}
_ json.Unmarshaler = (*Omittable[struct{}])(nil)
)
func OmittableOf[T any](value T) Omittable[T] {
return Omittable[T]{
value: value,
@ -33,3 +40,19 @@ func (o Omittable[T]) ValueOK() (T, bool) {
func (o Omittable[T]) IsSet() bool {
return o.set
}
func (o Omittable[T]) MarshalJSON() ([]byte, error) {
if !o.set {
return []byte("null"), nil
}
return json.Marshal(o.value)
}
func (o *Omittable[T]) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, &o.value)
if err != nil {
return err
}
o.set = true
return nil
}

View File

@ -1,10 +1,13 @@
package playground
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
)
// NOTE: New version available at https://embeddable-sandbox.cdn.apollographql.com/ -->
var apolloSandboxPage = template.Must(template.New("ApolloSandbox").Parse(`<!doctype html>
<html>
@ -23,40 +26,161 @@ var apolloSandboxPage = template.Must(template.New("ApolloSandbox").Parse(`<!doc
<body>
<div style="width: 100vw; height: 100vh;" id='embedded-sandbox'></div>
<!-- NOTE: New version available at https://embeddable-sandbox.cdn.apollographql.com/ -->
<script rel="preload" as="script" crossorigin="anonymous" integrity="{{.mainSRI}}" type="text/javascript" src="https://embeddable-sandbox.cdn.apollographql.com/7212121cad97028b007e974956dc951ce89d683c/embeddable-sandbox.umd.production.min.js"></script>
<script rel="preload" as="script" crossorigin="anonymous" integrity="{{.mainSRI}}" type="text/javascript" src="https://embeddable-sandbox.cdn.apollographql.com/02e2da0fccbe0240ef03d2396d6c98559bab5b06/embeddable-sandbox.umd.production.min.js"></script>
<script>
{{- if .endpointIsAbsolute}}
const url = {{.endpoint}};
{{- else}}
const url = location.protocol + '//' + location.host + {{.endpoint}};
{{- end}}
<!-- See https://www.apollographql.com/docs/graphos/explorer/sandbox/#options -->
new window.EmbeddedSandbox({
target: '#embedded-sandbox',
initialEndpoint: url,
const options = JSON.parse({{.options}})
new window.EmbeddedSandbox({
target: '#embedded-sandbox',
initialEndpoint: url,
persistExplorerState: true,
initialState: {
includeCookies: true,
pollForSchemaUpdates: false,
}
});
...options,
});
</script>
</body>
</html>`))
// ApolloSandboxHandler responsible for setting up the altair playground
func ApolloSandboxHandler(title, endpoint string) http.HandlerFunc {
// ApolloSandboxHandler responsible for setting up the apollo sandbox playground
func ApolloSandboxHandler(title, endpoint string, opts ...ApolloSandboxOption) http.HandlerFunc {
options := &apolloSandboxOptions{
HideCookieToggle: true,
InitialState: apolloSandboxInitialState{
IncludeCookies: true,
PollForSchemaUpdates: false,
},
}
for _, opt := range opts {
opt(options)
}
optionsBytes, err := json.Marshal(options)
if err != nil {
panic(fmt.Errorf("failed to marshal apollo sandbox options: %w", err))
}
return func(w http.ResponseWriter, r *http.Request) {
err := apolloSandboxPage.Execute(w, map[string]interface{}{
"title": title,
"endpoint": endpoint,
"endpointIsAbsolute": endpointHasScheme(endpoint),
"mainSRI": "sha256-ldbSJ7EovavF815TfCN50qKB9AMvzskb9xiG71bmg2I=",
"mainSRI": "sha256-pYhw/8TGkZxk960PMMpDtjhw9YtKXUzGv6XQQaMJSh8=",
"options": string(optionsBytes),
})
if err != nil {
panic(err)
}
}
}
// See https://www.apollographql.com/docs/graphos/explorer/sandbox/#options -->
type apolloSandboxOptions struct {
HideCookieToggle bool `json:"hideCookieToggle"`
EndpointIsEditable bool `json:"endpointIsEditable"`
InitialState apolloSandboxInitialState `json:"initialState,omitempty"`
}
type apolloSandboxInitialState struct {
IncludeCookies bool `json:"includeCookies"`
Document string `json:"document,omitempty"`
Variables map[string]any `json:"variables,omitempty"`
Headers map[string]any `json:"headers,omitempty"`
CollectionId string `json:"collectionId,omitempty"`
OperationId string `json:"operationId,omitempty"`
PollForSchemaUpdates bool `json:"pollForSchemaUpdates"`
SharedHeaders map[string]any `json:"sharedHeaders,omitempty"`
}
type ApolloSandboxOption func(options *apolloSandboxOptions)
// WithApolloSandboxHideCookieToggle By default, the embedded Sandbox does not show the Include cookies toggle in its connection settings.
//
// Set hideCookieToggle to false to enable users of your embedded Sandbox instance to toggle the Include cookies setting.
func WithApolloSandboxHideCookieToggle(hideCookieToggle bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.HideCookieToggle = hideCookieToggle
}
}
// WithApolloSandboxEndpointIsEditable By default, the embedded Sandbox has a URL input box that is editable by users.
//
// Set endpointIsEditable to false to prevent users of your embedded Sandbox instance from changing the endpoint URL.
func WithApolloSandboxEndpointIsEditable(endpointIsEditable bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.EndpointIsEditable = endpointIsEditable
}
}
// WithApolloSandboxInitialStateIncludeCookies Set this value to true if you want the Sandbox to pass { credentials: 'include' } for its requests by default.
//
// If you set hideCookieToggle to false, users can override this default setting with the Include cookies toggle. (By default, the embedded Sandbox does not show the Include cookies toggle in its connection settings.)
//
// If you also pass the handleRequest option, this option is ignored.
//
// Read more about the fetch API and credentials here https://developer.mozilla.org/en-US/docs/Web/API/fetch#credentials
func WithApolloSandboxInitialStateIncludeCookies(includeCookies bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.IncludeCookies = includeCookies
}
}
// WithApolloSandboxInitialStateDocument Document operation to populate in the Sandbox's editor on load.
//
// If you omit this, the Sandbox initially loads an example query based on your schema.
func WithApolloSandboxInitialStateDocument(document string) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.Document = document
}
}
// WithApolloSandboxInitialStateVariables Variables containing initial variable values to populate in the Sandbox on load.
//
// If provided, these variables should apply to the initial query you provide for document.
func WithApolloSandboxInitialStateVariables(variables map[string]any) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.Variables = variables
}
}
// WithApolloSandboxInitialStateHeaders Headers containing initial variable values to populate in the Sandbox on load.
//
// If provided, these variables should apply to the initial query you provide for document.
func WithApolloSandboxInitialStateHeaders(headers map[string]any) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.Headers = headers
}
}
// WithApolloSandboxInitialStateCollectionIdAndOperationId The ID of a collection, paired with an operation ID to populate in the Sandbox on load.
//
// You can find these values from a registered graph in Studio by clicking the ... menu next to an operation in the Explorer of that graph and selecting View operation details.
func WithApolloSandboxInitialStateCollectionIdAndOperationId(collectionId, operationId string) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.CollectionId = collectionId
options.InitialState.OperationId = operationId
}
}
// WithApolloSandboxInitialStatePollForSchemaUpdates If true, the embedded Sandbox periodically polls your initialEndpoint for schema updates.
//
// The default value is false.
func WithApolloSandboxInitialStatePollForSchemaUpdates(pollForSchemaUpdates bool) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.PollForSchemaUpdates = pollForSchemaUpdates
}
}
// WithApolloSandboxInitialStateSharedHeaders Headers that are applied by default to every operation executed by the embedded Sandbox.
//
// Users can disable the application of these headers, but they can't modify their values.
//
// The embedded Sandbox always includes these headers in its introspection queries to your initialEndpoint.
func WithApolloSandboxInitialStateSharedHeaders(sharedHeaders map[string]any) ApolloSandboxOption {
return func(options *apolloSandboxOptions) {
options.InitialState.SharedHeaders = sharedHeaders
}
}

View File

@ -58,13 +58,24 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:';
const subscriptionUrl = wsProto + '//' + location.host + {{.endpoint}};
{{- end}}
{{- if .fetcherHeaders}}
const fetcherHeaders = {{.fetcherHeaders}};
{{- else}}
const fetcherHeaders = undefined;
{{- end}}
{{- if .uiHeaders}}
const uiHeaders = {{.uiHeaders}};
{{- else}}
const uiHeaders = undefined;
{{- end}}
const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl });
const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl, headers: fetcherHeaders });
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: fetcher,
isHeadersEditorEnabled: true,
shouldPersistHeaders: true
shouldPersistHeaders: true,
headers: JSON.stringify(uiHeaders, null, 2)
}),
document.getElementById('graphiql'),
);
@ -75,16 +86,25 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
// Handler responsible for setting up the playground
func Handler(title string, endpoint string) http.HandlerFunc {
return HandlerWithHeaders(title, endpoint, nil, nil)
}
// HandlerWithHeaders sets up the playground.
// fetcherHeaders are used by the playground's fetcher instance and will not be visible in the UI.
// uiHeaders are default headers that will show up in the UI headers editor.
func HandlerWithHeaders(title string, endpoint string, fetcherHeaders map[string]string, uiHeaders map[string]string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "text/html; charset=UTF-8")
err := page.Execute(w, map[string]interface{}{
"title": title,
"endpoint": endpoint,
"fetcherHeaders": fetcherHeaders,
"uiHeaders": uiHeaders,
"endpointIsAbsolute": endpointHasScheme(endpoint),
"subscriptionEndpoint": getSubscriptionEndpoint(endpoint),
"version": "3.0.1",
"version": "3.0.6",
"cssSRI": "sha256-wTzfn13a+pLMB5rMeysPPR1hO7x0SwSeQI+cnw7VdbE=",
"jsSRI": "sha256-dLnxjV+d2rFUCtYKjbPy413/8O+Ahy7QqAhaPNlL8fk=",
"jsSRI": "sha256-eNxH+Ah7Z9up9aJYTQycgyNuy953zYZwE9Rqf5rH+r4=",
"reactSRI": "sha256-S0lp+k7zWUMk2ixteM6HZvu8L9Eh//OVrt+ZfbCpmgY=",
"reactDOMSRI": "sha256-IXWO0ITNDjfnNXIu5POVfqlgYoop36bDzhodR6LW5Pc=",
})

View File

@ -2,6 +2,7 @@ package graphql
import (
"encoding/json"
"errors"
"fmt"
"io"
"strconv"
@ -19,8 +20,16 @@ func UnmarshalUint(v interface{}) (uint, error) {
u64, err := strconv.ParseUint(v, 10, 64)
return uint(u64), err
case int:
if v < 0 {
return 0, errors.New("cannot convert negative numbers to uint")
}
return uint(v), nil
case int64:
if v < 0 {
return 0, errors.New("cannot convert negative numbers to uint")
}
return uint(v), nil
case json.Number:
u64, err := strconv.ParseUint(string(v), 10, 64)
@ -41,8 +50,16 @@ func UnmarshalUint64(v interface{}) (uint64, error) {
case string:
return strconv.ParseUint(v, 10, 64)
case int:
if v < 0 {
return 0, errors.New("cannot convert negative numbers to uint64")
}
return uint64(v), nil
case int64:
if v < 0 {
return 0, errors.New("cannot convert negative numbers to uint64")
}
return uint64(v), nil
case json.Number:
return strconv.ParseUint(string(v), 10, 64)
@ -66,8 +83,16 @@ func UnmarshalUint32(v interface{}) (uint32, error) {
}
return uint32(iv), nil
case int:
if v < 0 {
return 0, errors.New("cannot convert negative numbers to uint32")
}
return uint32(v), nil
case int64:
if v < 0 {
return 0, errors.New("cannot convert negative numbers to uint32")
}
return uint32(v), nil
case json.Number:
iv, err := strconv.ParseUint(string(v), 10, 32)

25
vendor/github.com/99designs/gqlgen/graphql/uuid.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
package graphql
import (
"fmt"
"github.com/google/uuid"
)
func MarshalUUID(id uuid.UUID) Marshaler {
if id == uuid.Nil {
return Null
}
return MarshalString(id.String())
}
func UnmarshalUUID(v any) (uuid.UUID, error) {
switch v := v.(type) {
case string:
return uuid.Parse(v)
case []byte:
return uuid.ParseBytes(v)
default:
return uuid.Nil, fmt.Errorf("%T is not a uuid", v)
}
}

View File

@ -1,3 +1,3 @@
package graphql
const Version = "v0.17.36"
const Version = "v0.17.45"

View File

@ -28,15 +28,37 @@ var mode = packages.NeedName |
packages.NeedModule |
packages.NeedDeps
// Packages is a wrapper around x/tools/go/packages that maintains a (hopefully prewarmed) cache of packages
// that can be invalidated as writes are made and packages are known to change.
type Packages struct {
packages map[string]*packages.Package
importToName map[string]string
loadErrors []error
type (
// Packages is a wrapper around x/tools/go/packages that maintains a (hopefully prewarmed) cache of packages
// that can be invalidated as writes are made and packages are known to change.
Packages struct {
packages map[string]*packages.Package
importToName map[string]string
loadErrors []error
buildFlags []string
numLoadCalls int // stupid test steam. ignore.
numNameCalls int // stupid test steam. ignore.
numLoadCalls int // stupid test steam. ignore.
numNameCalls int // stupid test steam. ignore.
}
// Option is a function that can be passed to NewPackages to configure the package loader
Option func(p *Packages)
)
// WithBuildTags adds build tags to the packages.Load call
func WithBuildTags(tags ...string) func(p *Packages) {
return func(p *Packages) {
p.buildFlags = append(p.buildFlags, "-tags", strings.Join(tags, ","))
}
}
// NewPackages creates a new packages cache
// It will load all packages in the current module, and any packages that are passed to Load or LoadAll
func NewPackages(opts ...Option) *Packages {
p := &Packages{}
for _, opt := range opts {
opt(p)
}
return p
}
func (p *Packages) CleanupUserPackages() {
@ -47,8 +69,8 @@ func (p *Packages) CleanupUserPackages() {
modInfo = nil
}
})
// Don't cleanup github.com/99designs/gqlgen prefixed packages, they haven't changed and do not need to be reloaded
// Don't cleanup github.com/99designs/gqlgen prefixed packages,
// they haven't changed and do not need to be reloaded
if modInfo != nil {
var toRemove []string
for k := range p.packages {
@ -56,7 +78,6 @@ func (p *Packages) CleanupUserPackages() {
toRemove = append(toRemove, k)
}
}
for _, k := range toRemove {
delete(p.packages, k)
}
@ -91,7 +112,10 @@ func (p *Packages) LoadAll(importPaths ...string) []*packages.Package {
if len(missing) > 0 {
p.numLoadCalls++
pkgs, err := packages.Load(&packages.Config{Mode: mode}, missing...)
pkgs, err := packages.Load(&packages.Config{
Mode: mode,
BuildFlags: p.buildFlags,
}, missing...)
if err != nil {
p.loadErrors = append(p.loadErrors, err)
}
@ -140,7 +164,10 @@ func (p *Packages) LoadWithTypes(importPath string) *packages.Package {
pkg := p.Load(importPath)
if pkg == nil || pkg.TypesInfo == nil {
p.numLoadCalls++
pkgs, err := packages.Load(&packages.Config{Mode: mode}, importPath)
pkgs, err := packages.Load(&packages.Config{
Mode: mode,
BuildFlags: p.buildFlags,
}, importPath)
if err != nil {
p.loadErrors = append(p.loadErrors, err)
return nil
@ -173,7 +200,10 @@ func (p *Packages) NameForPackage(importPath string) string {
if pkg == nil {
// otherwise do a name only lookup for it but don't put it in the package cache.
p.numNameCalls++
pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName}, importPath)
pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName,
BuildFlags: p.buildFlags,
}, importPath)
if err != nil {
p.loadErrors = append(p.loadErrors, err)
} else {

View File

@ -10,10 +10,10 @@ import (
"go/token"
"strings"
"github.com/99designs/gqlgen/internal/code"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/imports"
"github.com/99designs/gqlgen/internal/code"
)
type visitFn func(node ast.Node)

View File

@ -10,8 +10,9 @@ import (
"strconv"
"strings"
"github.com/99designs/gqlgen/internal/code"
"golang.org/x/tools/go/packages"
"github.com/99designs/gqlgen/internal/code"
)
type Rewriter struct {

View File

@ -1,5 +1,7 @@
package main
//go:generate sh generate_examples.sh
import (
"bytes"
_ "embed"
@ -12,12 +14,13 @@ import (
"os"
"path/filepath"
"github.com/urfave/cli/v2"
"github.com/99designs/gqlgen/api"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/internal/code"
"github.com/99designs/gqlgen/plugin/servergen"
"github.com/urfave/cli/v2"
)
//go:embed init-templates/schema.graphqls

View File

@ -3,10 +3,11 @@ package federation
import (
"go/types"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/plugin/federation/fieldset"
"github.com/vektah/gqlparser/v2/ast"
)
// Entity represents a federated type
@ -17,6 +18,7 @@ type Entity struct {
Resolvers []*EntityResolver
Requires []*Requires
Multi bool
Type types.Type
}
type EntityResolver struct {
@ -115,3 +117,8 @@ func (e *Entity) keyFields() []string {
}
return keyFields
}
// GetTypeInfo - get the imported package & type name combo. package.TypeName
func (e Entity) GetTypeInfo() string {
return templates.CurrentImports.LookupType(e.Type)
}

View File

@ -11,6 +11,7 @@ import (
"github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/internal/rewrite"
"github.com/99designs/gqlgen/plugin"
"github.com/99designs/gqlgen/plugin/federation/fieldset"
)
@ -18,9 +19,13 @@ import (
//go:embed federation.gotpl
var federationTemplate string
//go:embed requires.gotpl
var explicitRequiresTemplate string
type federation struct {
Entities []*Entity
Version int
Entities []*Entity
Version int
PackageOptions map[string]bool
}
// New returns a federation plugin that injects
@ -59,6 +64,12 @@ func (f *federation) MutateConfig(cfg *config.Config) error {
"_Any": {
Model: config.StringList{"github.com/99designs/gqlgen/graphql.Map"},
},
"federation__Scope": {
Model: config.StringList{"github.com/99designs/gqlgen/graphql.String"},
},
"federation__Policy": {
Model: config.StringList{"github.com/99designs/gqlgen/graphql.String"},
},
}
for typeName, entry := range builtins {
@ -80,6 +91,11 @@ func (f *federation) MutateConfig(cfg *config.Config) error {
cfg.Directives["tag"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["override"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["inaccessible"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["authenticated"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["requiresScopes"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["policy"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["interfaceObject"] = config.DirectiveConfig{SkipRuntime: true}
cfg.Directives["composeDirective"] = config.DirectiveConfig{SkipRuntime: true}
}
return nil
@ -101,6 +117,7 @@ func (f *federation) InjectSourceEarly() *ast.Source {
`
} else if f.Version == 2 {
input += `
directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
directive @composeDirective(name: String!) repeatable on SCHEMA
directive @extends on OBJECT | INTERFACE
directive @external on OBJECT | FIELD_DEFINITION
@ -118,9 +135,21 @@ func (f *federation) InjectSourceEarly() *ast.Source {
| UNION
directive @interfaceObject on OBJECT
directive @link(import: [String!], url: String!) repeatable on SCHEMA
directive @override(from: String!) on FIELD_DEFINITION
directive @override(from: String!, label: String) on FIELD_DEFINITION
directive @policy(policies: [[federation__Policy!]!]!) on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
directive @provides(fields: FieldSet!) on FIELD_DEFINITION
directive @requires(fields: FieldSet!) on FIELD_DEFINITION
directive @requiresScopes(scopes: [[federation__Scope!]!]!) on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM
directive @shareable repeatable on FIELD_DEFINITION | OBJECT
directive @tag(name: String!) repeatable on
| ARGUMENT_DEFINITION
@ -135,6 +164,8 @@ func (f *federation) InjectSourceEarly() *ast.Source {
| UNION
scalar _Any
scalar FieldSet
scalar federation__Policy
scalar federation__Scope
`
}
return &ast.Source{
@ -170,10 +201,14 @@ func (f *federation) InjectSourceLate(schema *ast.Schema) *ast.Source {
}
entityResolverInputDefinitions += "input " + r.InputTypeName + " {\n"
for _, keyField := range r.KeyFields {
entityResolverInputDefinitions += fmt.Sprintf("\t%s: %s\n", keyField.Field.ToGo(), keyField.Definition.Type.String())
entityResolverInputDefinitions += fmt.Sprintf(
"\t%s: %s\n",
keyField.Field.ToGo(),
keyField.Definition.Type.String(),
)
}
entityResolverInputDefinitions += "}"
resolvers += fmt.Sprintf("\t%s(reps: [%s!]!): [%s]\n", r.ResolverName, r.InputTypeName, e.Name)
resolvers += fmt.Sprintf("\t%s(reps: [%s]!): [%s]\n", r.ResolverName, r.InputTypeName, e.Name)
} else {
resolverArgs := ""
for _, keyField := range r.KeyFields {
@ -233,6 +268,16 @@ type Entity {
}
func (f *federation) GenerateCode(data *codegen.Data) error {
// requires imports
requiresImports := make(map[string]bool, 0)
requiresImports["context"] = true
requiresImports["fmt"] = true
requiresEntities := make(map[string]*Entity, 0)
// Save package options on f for template use
f.PackageOptions = data.Config.Federation.Options
if len(f.Entities) > 0 {
if data.Objects.ByName("Entity") != nil {
data.Objects.ByName("Entity").Root = true
@ -272,9 +317,19 @@ func (f *federation) GenerateCode(data *codegen.Data) error {
fmt.Println("skipping @requires field " + reqField.Name + " in " + e.Def.Name)
continue
}
// keep track of which entities have requires
requiresEntities[e.Def.Name] = e
// make a proper import path
typeString := strings.Split(obj.Type.String(), ".")
requiresImports[strings.Join(typeString[:len(typeString)-1], ".")] = true
cgField := reqField.Field.TypeReference(obj, data.Objects)
reqField.Type = cgField.TypeReference
}
// add type info to entity
e.Type = obj.Type
}
}
@ -295,10 +350,82 @@ func (f *federation) GenerateCode(data *codegen.Data) error {
}
}
if data.Config.Federation.Options["explicit_requires"] && len(requiresEntities) > 0 {
// check for existing requires functions
type Populator struct {
FuncName string
Exists bool
Comment string
Implementation string
Entity *Entity
}
populators := make([]Populator, 0)
rewriter, err := rewrite.New(data.Config.Federation.Dir())
if err != nil {
return err
}
for name, entity := range requiresEntities {
populator := Populator{
FuncName: fmt.Sprintf("Populate%sRequires", name),
Entity: entity,
}
populator.Comment = strings.TrimSpace(strings.TrimLeft(rewriter.GetMethodComment("executionContext", populator.FuncName), `\`))
populator.Implementation = strings.TrimSpace(rewriter.GetMethodBody("executionContext", populator.FuncName))
if populator.Implementation == "" {
populator.Exists = false
populator.Implementation = fmt.Sprintf("panic(fmt.Errorf(\"not implemented: %v\"))", populator.FuncName)
}
populators = append(populators, populator)
}
sort.Slice(populators, func(i, j int) bool {
return populators[i].FuncName < populators[j].FuncName
})
requiresFile := data.Config.Federation.Dir() + "/federation.requires.go"
existingImports := rewriter.ExistingImports(requiresFile)
for _, imp := range existingImports {
if imp.Alias == "" {
// import exists in both places, remove
delete(requiresImports, imp.ImportPath)
}
}
for k := range requiresImports {
existingImports = append(existingImports, rewrite.Import{ImportPath: k})
}
// render requires populators
err = templates.Render(templates.Options{
PackageName: data.Config.Federation.Package,
Filename: requiresFile,
Data: struct {
federation
ExistingImports []rewrite.Import
Populators []Populator
OriginalSource string
}{*f, existingImports, populators, ""},
GeneratedHeader: false,
Packages: data.Config.Packages,
Template: explicitRequiresTemplate,
})
if err != nil {
return err
}
}
return templates.Render(templates.Options{
PackageName: data.Config.Federation.Package,
Filename: data.Config.Federation.Filename,
Data: f,
PackageName: data.Config.Federation.Package,
Filename: data.Config.Federation.Filename,
Data: struct {
federation
UsePointers bool
}{*f, data.Config.ResolversAlwaysReturnPointers},
GeneratedHeader: true,
Packages: data.Config.Packages,
Template: federationTemplate,

View File

@ -5,6 +5,8 @@
{{ reserveImport "sync" }}
{{ reserveImport "github.com/99designs/gqlgen/plugin/federation/fedruntime" }}
{{ $options := .PackageOptions }}
{{ $usePointers := .UsePointers }}
var (
ErrUnknownType = errors.New("unknown type")
@ -103,11 +105,18 @@ func (ec *executionContext) __resolve_entities(ctx context.Context, representati
if err != nil {
return fmt.Errorf(`resolving Entity "{{$entity.Def.Name}}": %w`, err)
}
{{ range $entity.Requires }}
entity.{{.Field.JoinGo `.`}}, err = ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
{{ if and (index $options "explicit_requires") $entity.Requires }}
err = ec.Populate{{$entity.Def.Name}}Requires(ctx, {{- if (not $usePointers) -}}&{{- end -}}entity, rep)
if err != nil {
return err
return fmt.Errorf(`populating requires for Entity "{{$entity.Def.Name}}": %w`, err)
}
{{- else }}
{{ range $entity.Requires }}
entity.{{.Field.JoinGo `.`}}, err = ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
if err != nil {
return err
}
{{- end }}
{{- end }}
list[idx[i]] = entity
return nil
@ -132,7 +141,13 @@ func (ec *executionContext) __resolve_entities(ctx context.Context, representati
{{ range $_, $entity := .Entities }}
{{ if and .Resolvers .Multi -}}
case "{{.Def.Name}}":
{{range $i, $_ := .Resolvers -}}
resolverName, err := entityResolverNameFor{{.Def.Name}}(ctx, reps[0])
if err != nil {
return fmt.Errorf(`finding resolver for Entity "{{.Def.Name}}": %w`, err)
}
switch resolverName {
{{ range $i, $resolver := .Resolvers }}
case "{{.ResolverName}}":
_reps := make([]*{{.LookupInputType}}, len(reps))
for i, rep := range reps {
@ -166,6 +181,9 @@ func (ec *executionContext) __resolve_entities(ctx context.Context, representati
}
return nil
{{ end }}
default:
return fmt.Errorf("unknown resolver: %s", resolverName)
}
{{ end }}
{{- end }}
default:

View File

@ -4,9 +4,10 @@ import (
"fmt"
"strings"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/vektah/gqlparser/v2/ast"
)
// Set represents a FieldSet that is used in federation directives @key and @requires.
@ -99,6 +100,7 @@ func (f Field) ToGoPrivate() string {
for i, field := range f {
if i == 0 {
field = trimArgumentFromFieldName(field)
ret += templates.ToGoPrivate(field)
continue
}
@ -133,7 +135,7 @@ func parseUnnestedKeyFieldSet(raw string, prefix []string) Set {
ret := Set{}
for _, s := range strings.Fields(raw) {
next := append(prefix[:], s) //nolint:gocritic // slicing out on purpose
next := append(prefix[0:len(prefix):len(prefix)], s) //nolint:gocritic // set cap=len in order to force slice reallocation
ret = append(ret, next)
}
return ret
@ -147,7 +149,7 @@ func extractSubs(str string) (string, string, string) {
if start < 0 || end < 0 {
panic("invalid key fieldSet: " + str)
}
return strings.TrimSpace(str[:start]), strings.TrimSpace(str[start+1 : end]), strings.TrimSpace(str[end+1:])
return trimArgumentFromFieldName(strings.TrimSpace(str[:start])), strings.TrimSpace(str[start+1 : end]), strings.TrimSpace(str[end+1:])
}
// matchingBracketIndex returns the index of the closing bracket, assuming an open bracket at start.
@ -173,9 +175,16 @@ func matchingBracketIndex(str string, start int) int {
func fieldByName(obj *codegen.Object, name string) *codegen.Field {
for _, field := range obj.Fields {
field.Name = trimArgumentFromFieldName(field.Name)
if field.Name == name {
return field
}
}
return nil
}
// trimArgumentFromFieldName removes any arguments from the field name.
// It removes any suffixes from the raw string, starting from the argument-open character `(`
func trimArgumentFromFieldName(raw string) string {
return strings.Split(raw, "(")[0]
}

View File

@ -37,3 +37,6 @@ func (r *entityResolver) FindManyMultiHellosByName(ctx context.Context, reps []*
/// <Your code to resolve the list of items>
}
```
**Note:**
If you are using `omit_slice_element_pointers: true` option in your config yaml, your `GetMany` resolver will still generate in the example above the same signature `FindManyMultiHellosByName(ctx context.Context, reps []*generated.ManyMultiHellosByNameInput) ([]*generated.MultiHello, error)`. But all other instances will continue to honor `omit_slice_element_pointers: true`

View File

@ -0,0 +1,20 @@
{{ range .ExistingImports }}
{{ if ne .Alias "" }}
{{ reserveImport .ImportPath .Alias }}
{{ else }}
{{ reserveImport .ImportPath }}
{{ end }}
{{ end }}
{{ range .Populators -}}
{{ if .Comment -}}
// {{.Comment}}
{{- else -}}
// {{.FuncName}} is the requires populator for the {{.Entity.Def.Name}} entity.
{{- end }}
func (ec *executionContext) {{.FuncName}}(ctx context.Context, entity *{{.Entity.GetTypeInfo}}, reps map[string]interface{}) error {
{{.Implementation}}
}
{{ end }}
{{ .OriginalSource }}

View File

@ -9,10 +9,11 @@ import (
"strings"
"text/template"
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/codegen/templates"
"github.com/99designs/gqlgen/plugin"
"github.com/vektah/gqlparser/v2/ast"
)
//go:embed models.gotpl
@ -52,6 +53,7 @@ type Interface struct {
Fields []*Field
Implements []string
OmitCheck bool
Models []*Object
}
type Object struct {
@ -66,10 +68,11 @@ type Field struct {
// Name is the field's name as it appears in the schema
Name string
// GoName is the field's name as it appears in the generated Go code
GoName string
Type types.Type
Tag string
Omittable bool
GoName string
Type types.Type
Tag string
IsResolver bool
Omittable bool
}
type Enum struct {
@ -129,9 +132,20 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error {
OmitCheck: cfg.OmitInterfaceChecks,
}
// if the interface has a key directive as an entity interface, allow it to implement _Entity
if schemaType.Directives.ForName("key") != nil {
it.Implements = append(it.Implements, "_Entity")
}
b.Interfaces = append(b.Interfaces, it)
case ast.Object, ast.InputObject:
if schemaType == cfg.Schema.Query || schemaType == cfg.Schema.Mutation || schemaType == cfg.Schema.Subscription {
if cfg.IsRoot(schemaType) {
if !cfg.OmitRootModels {
b.Models = append(b.Models, &Object{
Description: schemaType.Description,
Name: schemaType.Name,
})
}
continue
}
@ -162,6 +176,7 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error {
uniqueMap[iface] = true
}
}
}
b.Models = append(b.Models, it)
@ -200,6 +215,18 @@ func (m *Plugin) MutateConfig(cfg *config.Config) error {
cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name))
}
for _, it := range b.Interfaces {
// On a given interface we want to keep a reference to all the models that implement it
for _, model := range b.Models {
for _, impl := range model.Implements {
if impl == it.Name {
// check if this isn't an implementation of an entity interface
if impl != "_Entity" {
// If this model has an implementation, add it to the Interface's Models
it.Models = append(it.Models, model)
}
}
}
}
cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name))
}
for _, it := range b.Scalars {
@ -393,6 +420,10 @@ func (m *Plugin) generateFields(cfg *config.Config, schemaType *ast.Definition)
f = mf
}
if f.IsResolver && cfg.OmitResolverFields {
continue
}
if f.Omittable {
if schemaType.Kind != ast.InputObject || field.Type.NonNull {
return nil, fmt.Errorf("generror: field %v.%v: omittable is only applicable to nullable input fields", schemaType.Name, field.Name)
@ -558,17 +589,19 @@ func removeDuplicateTags(t string) string {
continue
}
processed[kv[0]] = true
key := kv[0]
value := strings.Join(kv[1:], ":")
processed[key] = true
if len(returnTags) > 0 {
returnTags = " " + returnTags
}
isContained := containsInvalidSpace(kv[1])
isContained := containsInvalidSpace(value)
if isContained {
panic(fmt.Errorf("tag value should not contain any leading or trailing spaces: %s", kv[1]))
panic(fmt.Errorf("tag value should not contain any leading or trailing spaces: %s", value))
}
returnTags = kv[0] + ":" + kv[1] + returnTags
returnTags = key + ":" + value + returnTags
}
return returnTags
@ -585,6 +618,12 @@ func GoFieldHook(td *ast.Definition, fd *ast.FieldDefinition, f *Field) (*Field,
}
}
if arg := goField.Arguments.ForName("forceResolver"); arg != nil {
if k, err := arg.Value.Value(nil); err == nil {
f.IsResolver = k.(bool)
}
}
if arg := goField.Arguments.ForName("omittable"); arg != nil {
if k, err := arg.Value.Value(nil); err == nil {
f.Omittable = k.(bool)

View File

@ -3,9 +3,10 @@
package plugin
import (
"github.com/vektah/gqlparser/v2/ast"
"github.com/99designs/gqlgen/codegen"
"github.com/99designs/gqlgen/codegen/config"
"github.com/vektah/gqlparser/v2/ast"
)
type Plugin interface {
@ -30,7 +31,7 @@ type LateSourceInjector interface {
InjectSourceLate(schema *ast.Schema) *ast.Source
}
// Implementer is used to generate code inside resolvers
// ResolverImplementer is used to generate code inside resolvers
type ResolverImplementer interface {
Implement(field *codegen.Field) string
}

View File

@ -68,7 +68,7 @@ func (m *Plugin) generateSingleFile(data *codegen.Data) error {
continue
}
resolver := Resolver{o, f, nil, "", `panic("not implemented")`}
resolver := Resolver{o, f, nil, "", `panic("not implemented")`, nil}
file.Resolvers = append(file.Resolvers, &resolver)
}
}
@ -110,9 +110,12 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
for _, o := range objects {
if o.HasResolvers() {
fn := gqlToResolverName(data.Config.Resolver.Dir(), o.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
fnCase := gqlToResolverName(data.Config.Resolver.Dir(), o.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
fn := strings.ToLower(fnCase)
if files[fn] == nil {
files[fn] = &File{}
files[fn] = &File{
name: fnCase,
}
}
caser := cases.Title(language.English, cases.NoLower)
@ -127,48 +130,46 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
structName := templates.LcFirst(o.Name) + templates.UcFirst(data.Config.Resolver.Type)
comment := strings.TrimSpace(strings.TrimLeft(rewriter.GetMethodComment(structName, f.GoFieldName), `\`))
implementation := strings.TrimSpace(rewriter.GetMethodBody(structName, f.GoFieldName))
if implementation == "" {
// Check for Implementer Plugin
var resolver_implementer plugin.ResolverImplementer
var exists bool
for _, p := range data.Plugins {
if p_cast, ok := p.(plugin.ResolverImplementer); ok {
resolver_implementer = p_cast
exists = true
break
}
}
if exists {
implementation = resolver_implementer.Implement(f)
} else {
implementation = fmt.Sprintf("panic(fmt.Errorf(\"not implemented: %v - %v\"))", f.GoFieldName, f.Name)
}
// use default implementation, if no implementation was previously used
implementation = fmt.Sprintf("panic(fmt.Errorf(\"not implemented: %v - %v\"))", f.GoFieldName, f.Name)
}
resolver := Resolver{o, f, rewriter.GetPrevDecl(structName, f.GoFieldName), comment, implementation}
fn := gqlToResolverName(data.Config.Resolver.Dir(), f.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
resolver := Resolver{o, f, rewriter.GetPrevDecl(structName, f.GoFieldName), comment, implementation, nil}
var implExists bool
for _, p := range data.Plugins {
rImpl, ok := p.(plugin.ResolverImplementer)
if !ok {
continue
}
if implExists {
return fmt.Errorf("multiple plugins implement ResolverImplementer")
}
implExists = true
resolver.ImplementationRender = rImpl.Implement
}
fnCase := gqlToResolverName(data.Config.Resolver.Dir(), f.Position.Src.Name, data.Config.Resolver.FilenameTemplate)
fn := strings.ToLower(fnCase)
if files[fn] == nil {
files[fn] = &File{}
files[fn] = &File{
name: fnCase,
}
}
files[fn].Resolvers = append(files[fn].Resolvers, &resolver)
}
}
for filename, file := range files {
file.imports = rewriter.ExistingImports(filename)
file.RemainingSource = rewriter.RemainingSource(filename)
for _, file := range files {
file.imports = rewriter.ExistingImports(file.name)
file.RemainingSource = rewriter.RemainingSource(file.name)
}
newResolverTemplate := resolverTemplate
if data.Config.Resolver.ResolverTemplate != "" {
newResolverTemplate = readResolverTemplate(data.Config.Resolver.ResolverTemplate)
}
for filename, file := range files {
for _, file := range files {
resolverBuild := &ResolverBuild{
File: file,
PackageName: data.Config.Resolver.Package,
@ -192,7 +193,7 @@ func (m *Plugin) generatePerSchema(data *codegen.Data) error {
err := templates.Render(templates.Options{
PackageName: data.Config.Resolver.Package,
FileNotice: fileNotice.String(),
Filename: filename,
Filename: file.name,
Data: resolverBuild,
Packages: data.Config.Packages,
Template: newResolverTemplate,
@ -230,6 +231,7 @@ type ResolverBuild struct {
}
type File struct {
name string
// These are separated because the type definition of the resolver object may live in a different file from the
// resolver method implementations, for example when extending a type in a different graphql schema file
Objects []*codegen.Object
@ -250,11 +252,19 @@ func (f *File) Imports() string {
}
type Resolver struct {
Object *codegen.Object
Field *codegen.Field
PrevDecl *ast.FuncDecl
Comment string
Implementation string
Object *codegen.Object
Field *codegen.Field
PrevDecl *ast.FuncDecl
Comment string
ImplementationStr string
ImplementationRender func(r *codegen.Field) string
}
func (r *Resolver) Implementation() string {
if r.ImplementationRender != nil {
return r.ImplementationRender(r.Field)
}
return r.ImplementationStr
}
func gqlToResolverName(base string, gqlname, filenameTmpl string) string {

View File

@ -1,10 +0,0 @@
language: go
script: ./test.sh
go:
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6

View File

@ -75,12 +75,12 @@ gron.BatchDue(exprs, ref)
To find out when is the cron due next (in near future):
```go
allowCurrent = true // includes current time as well
nextTime, err := gron.NextTick(expr, allowCurrent) // gives time.Time, error
nextTime, err := gronx.NextTick(expr, allowCurrent) // gives time.Time, error
// OR, next tick after certain reference time
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // excludes the ref time
nextTime, err := gron.NextTickAfter(expr, refTime, allowCurrent) // gives time.Time, error
nextTime, err := gronx.NextTickAfter(expr, refTime, allowCurrent) // gives time.Time, error
```
### Prev Tick
@ -88,12 +88,12 @@ nextTime, err := gron.NextTickAfter(expr, refTime, allowCurrent) // gives time.T
To find out when was the cron due previously (in near past):
```go
allowCurrent = true // includes current time as well
prevTime, err := gron.PrevTick(expr, allowCurrent) // gives time.Time, error
prevTime, err := gronx.PrevTick(expr, allowCurrent) // gives time.Time, error
// OR, prev tick before certain reference time
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // excludes the ref time
nextTime, err := gron.PrevTickBefore(expr, refTime, allowCurrent) // gives time.Time, error
nextTime, err := gronx.PrevTickBefore(expr, refTime, allowCurrent) // gives time.Time, error
```
> The working of `PrevTick*()` and `NextTick*()` are mostly the same except the direction.
@ -304,8 +304,8 @@ Following modifiers supported
- `L` stands for last day of month (eg: `L` could mean 29th for February in leap year)
- `W` stands for closest week day (eg: `10W` is closest week days (MON-FRI) to 10th date)
- *Day of Week / 5th of 5 segments / 6th of 6+ segments:*
- `L` stands for last weekday of month (eg: `2L` is last monday)
- `#` stands for nth day of week in the month (eg: `1#2` is second sunday)
- `L` stands for last weekday of month (eg: `2L` is last tuesday)
- `#` stands for nth day of week in the month (eg: `1#2` is second monday)
---
## License

View File

@ -34,10 +34,10 @@ func (c *SegmentChecker) SetRef(ref time.Time) {
func (c *SegmentChecker) CheckDue(segment string, pos int) (due bool, err error) {
ref, last := c.GetRef(), -1
val, loc := valueByPos(ref, pos), ref.Location()
isMonth, isWeekDay := pos == 3, pos == 5
isMonthDay, isWeekDay := pos == 3, pos == 5
for _, offset := range strings.Split(segment, ",") {
mod := (isMonth || isWeekDay) && strings.ContainsAny(offset, "LW#")
mod := (isMonthDay || isWeekDay) && strings.ContainsAny(offset, "LW#")
if due, err = c.isOffsetDue(offset, val, pos); due || (!mod && err != nil) {
return
}
@ -45,9 +45,9 @@ func (c *SegmentChecker) CheckDue(segment string, pos int) (due bool, err error)
continue
}
if last == -1 {
last = time.Date(ref.Year(), ref.Month(), 1, 0, 0, 0, 0, loc).AddDate(0, 1, 0).Add(-time.Nanosecond).Day()
last = time.Date(ref.Year(), ref.Month(), 1, 0, 0, 0, 0, loc).AddDate(0, 1, 0).Add(-time.Second).Day()
}
if isMonth {
if isMonthDay {
due, err = isValidMonthDay(offset, last, ref)
} else if isWeekDay {
due, err = isValidWeekDay(offset, last, ref)
@ -81,14 +81,14 @@ func (c *SegmentChecker) isOffsetDue(offset string, val, pos int) (bool, error)
return false, err
}
if !isWeekDay && (val == 0 || nval == 0) {
return nval == 0 && val == 0, nil
}
if nval < bounds[0] || nval > bounds[1] {
return false, fmt.Errorf("segment#%d: '%s' out of bounds(%d, %d)", pos, offset, bounds[0], bounds[1])
}
if !isWeekDay && (val == 0 || nval == 0) {
return nval == 0 && val == 0, nil
}
return nval == val || (isWeekDay && nval == 7 && val == 0), nil
}
@ -126,7 +126,7 @@ func boundsByPos(pos int) (bounds []int) {
case 5:
bounds = []int{0, 7}
case 6:
bounds = []int{1, 9999}
bounds = []int{0, 9999}
}
return
}

View File

@ -29,6 +29,23 @@ var expressions = map[string]string{
"@everysecond": "* * * * * *",
}
// AddTag adds a new custom tag representing given expr
func AddTag(tag, expr string) error {
_, ok := expressions[tag]
if ok {
return errors.New("conflict tag")
}
segs, err := Segments(expr)
if err != nil {
return err
}
expr = strings.Join(segs, " ")
expressions[tag] = expr
return nil
}
// SpaceRe is regex for whitespace.
var SpaceRe = regexp.MustCompile(`\s+`)
var yearRe = regexp.MustCompile(`\d{4}`)
@ -51,8 +68,8 @@ type Gronx struct {
}
// New initializes Gronx with factory defaults.
func New() Gronx {
return Gronx{&SegmentChecker{}}
func New() *Gronx {
return &Gronx{&SegmentChecker{}}
}
// IsDue checks if cron expression is due for given reference time (or now).
@ -96,11 +113,42 @@ func Segments(expr string) ([]string, error) {
// SegmentsDue checks if all cron parts are due.
// It returns bool. You should use IsDue(expr) instead.
func (g *Gronx) SegmentsDue(segs []string) (bool, error) {
for pos, seg := range segs {
skipMonthDayCheck := false
for i := 0; i < len(segs); i++ {
pos := len(segs) - 1 - i
seg := segs[pos]
isMonthDay, isWeekday := pos == 3, pos == 5
if seg == "*" || seg == "?" {
continue
}
if isMonthDay && skipMonthDayCheck {
continue
}
if isWeekday {
monthDaySeg := segs[3]
intersect := strings.Index(seg, "*/") == 0 || strings.Index(monthDaySeg, "*") == 0 || monthDaySeg == "?"
if !intersect {
due, err := g.C.CheckDue(seg, pos)
if err != nil {
return false, err
}
monthDayDue, err := g.C.CheckDue(monthDaySeg, 3)
if due || monthDayDue {
skipMonthDayCheck = true
continue
}
if err != nil {
return false, err
}
}
}
if due, err := g.C.CheckDue(seg, pos); !due {
return due, err
}
@ -109,6 +157,9 @@ func (g *Gronx) SegmentsDue(segs []string) (bool, error) {
return true, nil
}
// checker for validity
var checker = &SegmentChecker{ref: time.Now()}
// IsValid checks if cron expression is valid.
// It returns bool.
func (g *Gronx) IsValid(expr string) bool {
@ -117,9 +168,8 @@ func (g *Gronx) IsValid(expr string) bool {
return false
}
g.C.SetRef(time.Now())
for pos, seg := range segs {
if _, err := g.C.CheckDue(seg, pos); err != nil {
if _, err := checker.CheckDue(seg, pos); err != nil {
return false
}
}

View File

@ -41,25 +41,79 @@ func NextTickAfter(expr string, start time.Time, inclRefTime bool) (time.Time, e
return next, err
}
func loop(gron Gronx, segments []string, start time.Time, incl bool, reverse bool) (next time.Time, err error) {
func loop(gron *Gronx, segments []string, start time.Time, incl bool, reverse bool) (next time.Time, err error) {
iter, next, bumped := 500, start, false
over:
for iter > 0 {
iter--
for pos, seg := range segments {
skipMonthDayForIter := false
for i := 0; i < len(segments); i++ {
pos := len(segments) - 1 - i
seg := segments[pos]
isMonthDay, isWeekday := pos == 3, pos == 5
if seg == "*" || seg == "?" {
continue
}
if next, bumped, err = bumpUntilDue(gron.C, seg, pos, next, reverse); bumped {
if !isWeekday {
if isMonthDay && skipMonthDayForIter {
continue
}
if next, bumped, err = bumpUntilDue(gron.C, seg, pos, next, reverse); bumped {
goto over
}
continue
}
// From here we process the weekday segment in case it is neither * nor ?
monthDaySeg := segments[3]
intersect := strings.Index(seg, "*/") == 0 || strings.Index(monthDaySeg, "*") == 0 || monthDaySeg == "?"
nextForWeekDay := next
nextForWeekDay, bumped, err = bumpUntilDue(gron.C, seg, pos, nextForWeekDay, reverse)
if !bumped {
// Weekday seg is specific and next is already at right weekday, so no need to process month day if union case
next = nextForWeekDay
if !intersect {
skipMonthDayForIter = true
}
continue
}
// Weekday was bumped, so we need to check for month day
if intersect {
// We need intersection so we keep bumped weekday and go over
next = nextForWeekDay
goto over
}
// Month day seg is specific and a number/list/range, so we need to check and keep the closest to next
nextForMonthDay := next
nextForMonthDay, bumped, err = bumpUntilDue(gron.C, monthDaySeg, 3, nextForMonthDay, reverse)
monthDayIsClosestToNextThanWeekDay := reverse && nextForMonthDay.After(nextForWeekDay) ||
!reverse && nextForMonthDay.Before(nextForWeekDay)
if monthDayIsClosestToNextThanWeekDay {
next = nextForMonthDay
if !bumped {
// Month day seg is specific and next is already at right month day, we can continue
skipMonthDayForIter = true
continue
}
} else {
next = nextForWeekDay
}
goto over
}
if !incl && next.Format(FullDateFormat) == start.Format(FullDateFormat) {
delta := time.Second
if reverse {
delta = -time.Second
}
next, _, err = bumpUntilDue(gron.C, segments[0], 0, next.Add(delta), reverse)
next = next.Add(delta)
continue
}
return
@ -105,31 +159,37 @@ func bumpUntilDue(c Checker, segment string, pos int, ref time.Time, reverse boo
if ok, _ := c.CheckDue(segment, pos); ok {
return ref, iter != limit[pos], nil
}
ref = bump(ref, pos, reverse)
if reverse {
ref = bumpReverse(ref, pos)
} else {
ref = bump(ref, pos)
}
iter--
}
return ref, false, errors.New("tried so hard")
}
func bump(ref time.Time, pos int, reverse bool) time.Time {
factor := 1
if reverse {
factor = -1
}
func bump(ref time.Time, pos int) time.Time {
loc := ref.Location()
switch pos {
case 0:
ref = ref.Add(time.Duration(factor) * time.Second)
ref = ref.Add(time.Second)
case 1:
ref = ref.Add(time.Duration(factor) * time.Minute)
minTime := ref.Add(time.Minute)
ref = time.Date(minTime.Year(), minTime.Month(), minTime.Day(), minTime.Hour(), minTime.Minute(), 0, 0, loc)
case 2:
ref = ref.Add(time.Duration(factor) * time.Hour)
hTime := ref.Add(time.Hour)
ref = time.Date(hTime.Year(), hTime.Month(), hTime.Day(), hTime.Hour(), 0, 0, 0, loc)
case 3, 5:
ref = ref.AddDate(0, 0, factor)
dTime := ref.AddDate(0, 0, 1)
ref = time.Date(dTime.Year(), dTime.Month(), dTime.Day(), 0, 0, 0, 0, loc)
case 4:
ref = ref.AddDate(0, factor, 0)
ref = time.Date(ref.Year(), ref.Month(), 1, 0, 0, 0, 0, loc)
ref = ref.AddDate(0, 1, 0)
case 6:
ref = ref.AddDate(factor, 0, 0)
yTime := ref.AddDate(1, 0, 0)
ref = time.Date(yTime.Year(), 1, 1, 0, 0, 0, 0, loc)
}
return ref
}

View File

@ -30,3 +30,28 @@ func PrevTickBefore(expr string, start time.Time, inclRefTime bool) (time.Time,
}
return prev, err
}
func bumpReverse(ref time.Time, pos int) time.Time {
loc := ref.Location()
switch pos {
case 0:
ref = ref.Add(-time.Second)
case 1:
minTime := ref.Add(-time.Minute)
ref = time.Date(minTime.Year(), minTime.Month(), minTime.Day(), minTime.Hour(), minTime.Minute(), 59, 0, loc)
case 2:
hTime := ref.Add(-time.Hour)
ref = time.Date(hTime.Year(), hTime.Month(), hTime.Day(), hTime.Hour(), 59, 59, 0, loc)
case 3, 5:
dTime := ref.AddDate(0, 0, -1)
ref = time.Date(dTime.Year(), dTime.Month(), dTime.Day(), 23, 59, 59, 0, loc)
case 4:
ref = time.Date(ref.Year(), ref.Month(), 1, 0, 0, 0, 0, loc)
ref = ref.Add(-time.Second)
case 6:
yTime := ref.AddDate(-1, 0, 0)
ref = time.Date(yTime.Year(), 12, 31, 23, 59, 59, 0, loc)
}
return ref
}

View File

@ -18,7 +18,10 @@ func inStep(val int, s string, bounds []int) (bool, error) {
return false, errors.New("step can't be 0")
}
if strings.Index(s, "*/") == 0 || strings.Index(s, "0/") == 0 {
if strings.Index(s, "*/") == 0 {
return (val-bounds[0])%step == 0, nil
}
if strings.Index(s, "0/") == 0 {
return val%step == 0, nil
}
@ -104,22 +107,20 @@ func isValidMonthDay(val string, last int, ref time.Time) (valid bool, err error
func isValidWeekDay(val string, last int, ref time.Time) (bool, error) {
loc := ref.Location()
if pos := strings.Index(strings.ReplaceAll(val, "7L", "0L"), "L"); pos > 0 {
if pos := strings.Index(val, "L"); pos > 0 {
nval, err := strconv.Atoi(val[0:pos])
if err != nil {
return false, err
}
for i := 0; i < 7; i++ {
decr := last - i
dref := time.Date(ref.Year(), ref.Month(), decr, ref.Hour(), ref.Minute(), ref.Second(), ref.Nanosecond(), loc)
if int(dref.Weekday()) == nval {
return ref.Day() == decr, nil
day := last - i
dref := time.Date(ref.Year(), ref.Month(), day, ref.Hour(), ref.Minute(), ref.Second(), 0, loc)
if int(dref.Weekday()) == nval%7 {
return ref.Day() == day, nil
}
}
return false, nil
}
pos := strings.Index(val, "#")

View File

@ -233,6 +233,9 @@ tlsConfig, err := certmagic.TLS([]string{"example.com"})
if err != nil {
return err
}
// be sure to customize NextProtos if serving a specific
// application protocol after the TLS handshake, for example:
tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...)
```

View File

@ -155,8 +155,8 @@ var AttemptsCtxKey retryStateCtxKey
// front. We figure that intermittent errors would be
// resolved after the first retry, but any errors after
// that would probably require at least a few minutes
// to clear up: either for DNS to propagate, for the
// administrator to fix their DNS or network properties,
// or hours to clear up: either for DNS to propagate, for
// the administrator to fix their DNS or network config,
// or some other external factor needs to change. We
// chose intervals that we think will be most useful
// without introducing unnecessary delay. The last
@ -168,13 +168,26 @@ var retryIntervals = []time.Duration{
2 * time.Minute,
5 * time.Minute, // elapsed: 10 min
10 * time.Minute,
20 * time.Minute,
10 * time.Minute,
10 * time.Minute,
20 * time.Minute, // elapsed: 1 hr
20 * time.Minute,
20 * time.Minute,
20 * time.Minute, // elapsed: 2 hr
30 * time.Minute,
30 * time.Minute, // elapsed: 2 hr
30 * time.Minute, // elapsed: 3 hr
30 * time.Minute,
30 * time.Minute, // elapsed: 4 hr
30 * time.Minute,
30 * time.Minute, // elapsed: 5 hr
1 * time.Hour, // elapsed: 6 hr
1 * time.Hour,
3 * time.Hour, // elapsed: 6 hr
6 * time.Hour, // for up to maxRetryDuration
1 * time.Hour, // elapsed: 8 hr
2 * time.Hour,
2 * time.Hour, // elapsed: 12 hr
3 * time.Hour,
3 * time.Hour, // elapsed: 18 hr
6 * time.Hour, // repeat for up to maxRetryDuration
}
// maxRetryDuration is the maximum duration to try

View File

@ -410,7 +410,8 @@ func SubjectIsIP(subj string) bool {
func SubjectIsInternal(subj string) bool {
return subj == "localhost" ||
strings.HasSuffix(subj, ".localhost") ||
strings.HasSuffix(subj, ".local")
strings.HasSuffix(subj, ".local") ||
strings.HasSuffix(subj, ".home.arpa")
}
// MatchWildcard returns true if subject (a candidate DNS name)

View File

@ -268,7 +268,7 @@ type OnDemandConfig struct {
// whether a certificate can be obtained or renewed
// for the given name. If an error is returned, the
// request will be denied.
DecisionFunc func(name string) error
DecisionFunc func(ctx context.Context, name string) error
// Sources for getting new, unmanaged certificates.
// They will be invoked only during TLS handshakes

View File

@ -50,7 +50,7 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif
}
func (cfg *Config) GetCertificateWithContext(ctx context.Context, clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
if err := cfg.emit(ctx, "tls_get_certificate", map[string]any{"client_hello": clientHello}); err != nil {
if err := cfg.emit(ctx, "tls_get_certificate", map[string]any{"client_hello": clientHelloWithoutConn(clientHello)}); err != nil {
cfg.Logger.Error("TLS handshake aborted by event handler",
zap.String("server_name", clientHello.ServerName),
zap.String("remote", clientHello.Conn.RemoteAddr().String()),
@ -58,6 +58,12 @@ func (cfg *Config) GetCertificateWithContext(ctx context.Context, clientHello *t
return nil, fmt.Errorf("handshake aborted by event handler: %w", err)
}
if ctx == nil {
// tests can't set context on a tls.ClientHelloInfo because it's unexported :(
ctx = context.Background()
}
ctx = context.WithValue(ctx, ClientHelloInfoCtxKey, clientHello)
// special case: serve up the certificate for a TLS-ALPN ACME challenge
// (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05)
for _, proto := range clientHello.SupportedProtos {
@ -313,8 +319,8 @@ func (cfg *Config) getCertDuringHandshake(ctx context.Context, hello *tls.Client
// Make sure a certificate is allowed for the given name. If not, it doesn't
// make sense to try loading one from storage (issue #185), getting it from a
// certificate manager, or obtaining one from an issuer.
if err := cfg.checkIfCertShouldBeObtained(name, false); err != nil {
return Certificate{}, fmt.Errorf("certificate is not allowed for server name %s: %v", name, err)
if err := cfg.checkIfCertShouldBeObtained(ctx, name, false); err != nil {
return Certificate{}, fmt.Errorf("certificate is not allowed for server name %s: %w", name, err)
}
// If an external Manager is configured, try to get it from them.
@ -438,7 +444,7 @@ func (cfg *Config) optionalMaintenance(ctx context.Context, log *zap.Logger, cer
// checkIfCertShouldBeObtained checks to see if an on-demand TLS certificate
// should be obtained for a given domain based upon the config settings. If
// a non-nil error is returned, do not issue a new certificate for name.
func (cfg *Config) checkIfCertShouldBeObtained(name string, requireOnDemand bool) error {
func (cfg *Config) checkIfCertShouldBeObtained(ctx context.Context, name string, requireOnDemand bool) error {
if requireOnDemand && cfg.OnDemand == nil {
return fmt.Errorf("not configured for on-demand certificate issuance")
}
@ -447,7 +453,7 @@ func (cfg *Config) checkIfCertShouldBeObtained(name string, requireOnDemand bool
}
if cfg.OnDemand != nil {
if cfg.OnDemand.DecisionFunc != nil {
if err := cfg.OnDemand.DecisionFunc(name); err != nil {
if err := cfg.OnDemand.DecisionFunc(ctx, name); err != nil {
return fmt.Errorf("decision func: %w", err)
}
return nil
@ -685,7 +691,7 @@ func (cfg *Config) renewDynamicCertificate(ctx context.Context, hello *tls.Clien
defer cancel()
// Make sure a certificate for this name should be renewed on-demand
err := cfg.checkIfCertShouldBeObtained(name, true)
err := cfg.checkIfCertShouldBeObtained(ctx, name, true)
if err != nil {
// if not, remove from cache (it will be deleted from storage later)
cfg.certCache.mu.Lock()
@ -875,3 +881,52 @@ var (
certLoadWaitChans = make(map[string]chan struct{})
certLoadWaitChansMu sync.Mutex
)
type serializableClientHello struct {
CipherSuites []uint16
ServerName string
SupportedCurves []tls.CurveID
SupportedPoints []uint8
SignatureSchemes []tls.SignatureScheme
SupportedProtos []string
SupportedVersions []uint16
RemoteAddr, LocalAddr net.Addr // values copied from the Conn as they are still useful/needed
conn net.Conn // unexported so it's not serialized
}
// clientHelloWithoutConn returns the data from the ClientHelloInfo without the
// pesky exported Conn field, which often causes an error when serializing because
// the underlying type may be unserializable.
func clientHelloWithoutConn(hello *tls.ClientHelloInfo) serializableClientHello {
if hello == nil {
return serializableClientHello{}
}
var remote, local net.Addr
if hello.Conn != nil {
remote = hello.Conn.RemoteAddr()
local = hello.Conn.LocalAddr()
}
return serializableClientHello{
CipherSuites: hello.CipherSuites,
ServerName: hello.ServerName,
SupportedCurves: hello.SupportedCurves,
SupportedPoints: hello.SupportedPoints,
SignatureSchemes: hello.SignatureSchemes,
SupportedProtos: hello.SupportedProtos,
SupportedVersions: hello.SupportedVersions,
RemoteAddr: remote,
LocalAddr: local,
conn: hello.Conn,
}
}
type helloInfoCtxKey string
// ClientHelloInfoCtxKey is the key by which the ClientHelloInfo can be extracted from
// a context.Context within a DecisionFunc. However, be advised that it is best practice
// that the decision whether to obtain a certificate is be based solely on the name,
// not other properties of the specific connection/client requesting the connection.
// For example, it is not adviseable to use a client's IP address to decide whether to
// allow a certificate. Instead, the ClientHello can be useful for logging, etc.
const ClientHelloInfoCtxKey helloInfoCtxKey = "certmagic:ClientHelloInfo"

View File

@ -17,9 +17,11 @@ package certmagic
import (
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"log"
"io/fs"
"path"
"runtime"
"strings"
@ -390,30 +392,117 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
// CleanStorageOptions specifies how to clean up a storage unit.
type CleanStorageOptions struct {
OCSPStaples bool
// Optional custom logger.
Logger *zap.Logger
// Optional ID of the instance initiating the cleaning.
InstanceID string
// If set, cleaning will be skipped if it was performed
// more recently than this interval.
Interval time.Duration
// Whether to clean cached OCSP staples.
OCSPStaples bool
// Whether to cleanup expired certificates, and if so,
// how long to let them stay after they've expired.
ExpiredCerts bool
ExpiredCertGracePeriod time.Duration
}
// CleanStorage removes assets which are no longer useful,
// according to opts.
func CleanStorage(ctx context.Context, storage Storage, opts CleanStorageOptions) {
func CleanStorage(ctx context.Context, storage Storage, opts CleanStorageOptions) error {
const (
lockName = "storage_clean"
storageKey = "last_clean.json"
)
if opts.Logger == nil {
opts.Logger = defaultLogger.Named("clean_storage")
}
opts.Logger = opts.Logger.With(zap.Any("storage", storage))
// storage cleaning should be globally exclusive
if err := storage.Lock(ctx, lockName); err != nil {
return fmt.Errorf("unable to acquire %s lock: %v", lockName, err)
}
defer func() {
if err := storage.Unlock(ctx, lockName); err != nil {
opts.Logger.Error("unable to release lock", zap.Error(err))
return
}
}()
// cleaning should not happen more often than the interval
if opts.Interval > 0 {
lastCleanBytes, err := storage.Load(ctx, storageKey)
if !errors.Is(err, fs.ErrNotExist) {
if err != nil {
return fmt.Errorf("loading last clean timestamp: %v", err)
}
var lastClean lastCleanPayload
err = json.Unmarshal(lastCleanBytes, &lastClean)
if err != nil {
return fmt.Errorf("decoding last clean data: %v", err)
}
lastTLSClean := lastClean["tls"]
if time.Since(lastTLSClean.Timestamp) < opts.Interval {
nextTime := time.Now().Add(opts.Interval)
opts.Logger.Warn("storage cleaning happened too recently; skipping for now",
zap.String("instance", lastTLSClean.InstanceID),
zap.Time("try_again", nextTime),
zap.Duration("try_again_in", time.Until(nextTime)),
)
return nil
}
}
}
opts.Logger.Info("cleaning storage unit")
if opts.OCSPStaples {
err := deleteOldOCSPStaples(ctx, storage)
err := deleteOldOCSPStaples(ctx, storage, opts.Logger)
if err != nil {
log.Printf("[ERROR] Deleting old OCSP staples: %v", err)
opts.Logger.Error("deleting old OCSP staples", zap.Error(err))
}
}
if opts.ExpiredCerts {
err := deleteExpiredCerts(ctx, storage, opts.ExpiredCertGracePeriod)
err := deleteExpiredCerts(ctx, storage, opts.Logger, opts.ExpiredCertGracePeriod)
if err != nil {
log.Printf("[ERROR] Deleting expired certificates: %v", err)
opts.Logger.Error("deleting expired certificates staples", zap.Error(err))
}
}
// TODO: delete stale locks?
// update the last-clean time
lastCleanBytes, err := json.Marshal(lastCleanPayload{
"tls": lastCleaned{
Timestamp: time.Now(),
InstanceID: opts.InstanceID,
},
})
if err != nil {
return fmt.Errorf("encoding last cleaned info: %v", err)
}
if err := storage.Store(ctx, storageKey, lastCleanBytes); err != nil {
return fmt.Errorf("storing last clean info: %v", err)
}
return nil
}
func deleteOldOCSPStaples(ctx context.Context, storage Storage) error {
type lastCleanPayload map[string]lastCleaned
type lastCleaned struct {
Timestamp time.Time `json:"timestamp"`
InstanceID string `json:"instance_id,omitempty"`
}
func deleteOldOCSPStaples(ctx context.Context, storage Storage, logger *zap.Logger) error {
ocspKeys, err := storage.List(ctx, prefixOCSP, false)
if err != nil {
// maybe just hasn't been created yet; no big deal
@ -428,7 +517,7 @@ func deleteOldOCSPStaples(ctx context.Context, storage Storage) error {
}
ocspBytes, err := storage.Load(ctx, key)
if err != nil {
log.Printf("[ERROR] While deleting old OCSP staples, unable to load staple file: %v", err)
logger.Error("while deleting old OCSP staples, unable to load staple file", zap.Error(err))
continue
}
resp, err := ocsp.ParseResponse(ocspBytes, nil)
@ -436,7 +525,7 @@ func deleteOldOCSPStaples(ctx context.Context, storage Storage) error {
// contents are invalid; delete it
err = storage.Delete(ctx, key)
if err != nil {
log.Printf("[ERROR] Purging corrupt staple file %s: %v", key, err)
logger.Error("purging corrupt staple file", zap.String("storage_key", key), zap.Error(err))
}
continue
}
@ -444,14 +533,14 @@ func deleteOldOCSPStaples(ctx context.Context, storage Storage) error {
// response has expired; delete it
err = storage.Delete(ctx, key)
if err != nil {
log.Printf("[ERROR] Purging expired staple file %s: %v", key, err)
logger.Error("purging expired staple file", zap.String("storage_key", key), zap.Error(err))
}
}
}
return nil
}
func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.Duration) error {
func deleteExpiredCerts(ctx context.Context, storage Storage, logger *zap.Logger, gracePeriod time.Duration) error {
issuerKeys, err := storage.List(ctx, prefixCerts, false)
if err != nil {
// maybe just hasn't been created yet; no big deal
@ -461,7 +550,7 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
for _, issuerKey := range issuerKeys {
siteKeys, err := storage.List(ctx, issuerKey, false)
if err != nil {
log.Printf("[ERROR] Listing contents of %s: %v", issuerKey, err)
logger.Error("listing contents", zap.String("issuer_key", issuerKey), zap.Error(err))
continue
}
@ -475,7 +564,7 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
siteAssets, err := storage.List(ctx, siteKey, false)
if err != nil {
log.Printf("[ERROR] Listing contents of %s: %v", siteKey, err)
logger.Error("listing site contents", zap.String("site_key", siteKey), zap.Error(err))
continue
}
@ -498,18 +587,23 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
}
if expiredTime := time.Since(expiresAt(cert)); expiredTime >= gracePeriod {
log.Printf("[INFO] Certificate %s expired %s ago; cleaning up", assetKey, expiredTime)
logger.Info("certificate expired beyond grace period; cleaning up",
zap.String("asset_key", assetKey),
zap.Duration("expired_for", expiredTime),
zap.Duration("grace_period", gracePeriod))
baseName := strings.TrimSuffix(assetKey, ".crt")
for _, relatedAsset := range []string{
assetKey,
baseName + ".key",
baseName + ".json",
} {
log.Printf("[INFO] Deleting %s because resource expired", relatedAsset)
logger.Info("deleting asset because resource expired", zap.String("asset_key", relatedAsset))
err := storage.Delete(ctx, relatedAsset)
if err != nil {
log.Printf("[ERROR] Cleaning up asset related to expired certificate for %s: %s: %v",
baseName, relatedAsset, err)
logger.Error("could not clean up asset related to expired certificate",
zap.String("base_name", baseName),
zap.String("related_asset", relatedAsset),
zap.Error(err))
}
}
}
@ -521,7 +615,7 @@ func deleteExpiredCerts(ctx context.Context, storage Storage, gracePeriod time.D
continue
}
if len(siteAssets) == 0 {
log.Printf("[INFO] Deleting %s because key is empty", siteKey)
logger.Info("deleting site folder because key is empty", zap.String("site_key", siteKey))
err := storage.Delete(ctx, siteKey)
if err != nil {
return fmt.Errorf("deleting empty site folder %s: %v", siteKey, err)

View File

@ -629,21 +629,23 @@ func robustTryListen(addr string) (net.Listener, error) {
return nil, nil
}
// hmm, we couldn't connect to the socket, so something else must
// be wrong, right? wrong!! we've had reports across multiple OSes
// now that sometimes connections fail even though the OS told us
// that the address was already in use; either the listener is
// fluctuating between open and closed very, very quickly, or the
// OS is inconsistent and contradicting itself; I have been unable
// to reproduce this, so I'm now resorting to hard-coding substring
// matching in error messages as a really hacky and unreliable
// safeguard against this, until we can idenify exactly what was
// happening; see the following threads for more info:
// Hmm, we couldn't connect to the socket, so something else must
// be wrong, right? wrong!! Apparently if a port is bound by another
// listener with a specific host, i.e. 'x:1234', we cannot bind to
// ':1234' -- it is considered a conflict, but 'y:1234' is not.
// I guess we need to assume the conflicting listener is properly
// configured and continue. But we should tell the user to specify
// the correct ListenHost to avoid conflict or at least so we can
// know that the user is intentional about that port and hopefully
// has an ACME solver on it.
//
// History:
// https://caddy.community/t/caddy-retry-error/7317
// https://caddy.community/t/v2-upgrade-to-caddy2-failing-with-errors/7423
// https://github.com/caddyserver/certmagic/issues/250
if strings.Contains(listenErr.Error(), "address already in use") ||
strings.Contains(listenErr.Error(), "one usage of each socket address") {
log.Printf("[WARNING] OS reports a contradiction: %v - but we cannot connect to it, with this error: %v; continuing anyway 🤞 (I don't know what causes this... if you do, please help?)", listenErr, connectErr)
log.Printf("[WARNING] %v - be sure to set the ACMEIssuer.ListenHost field; assuming conflicting listener is correctly configured and continuing", listenErr)
return nil, nil
}
}

View File

@ -133,7 +133,7 @@ type Locker interface {
// is released or becomes stale.
//
// If the named lock represents an idempotent operation, callers
// should awlays check to make sure the work still needs to be
// should always check to make sure the work still needs to be
// completed after acquiring the lock. You never know if another
// process already completed the task while you were waiting to
// acquire it.

354
vendor/github.com/casbin/casbin/v2/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,354 @@
# Based on https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322
# This code is licensed under the terms of the MIT license https://opensource.org/license/mit
# Copyright (c) 2021 Marat Reymers
## Golden config for golangci-lint v1.56.2
#
# This is the best config for golangci-lint based on my experience and opinion.
# It is very strict, but not extremely strict.
# Feel free to adapt and change it for your needs.
run:
# Timeout for analysis, e.g. 30s, 5m.
# Default: 1m
timeout: 3m
# This file contains only configs which differ from defaults.
# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
linters-settings:
cyclop:
# The maximal code complexity to report.
# Default: 10
max-complexity: 30
# The maximal average package complexity.
# If it's higher than 0.0 (float) the check is enabled
# Default: 0.0
package-average: 10.0
errcheck:
# Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
# Such cases aren't reported by default.
# Default: false
check-type-assertions: true
exhaustive:
# Program elements to check for exhaustiveness.
# Default: [ switch ]
check:
- switch
- map
exhaustruct:
# List of regular expressions to exclude struct packages and their names from checks.
# Regular expressions must match complete canonical struct package/name/structname.
# Default: []
exclude:
# std libs
- "^net/http.Client$"
- "^net/http.Cookie$"
- "^net/http.Request$"
- "^net/http.Response$"
- "^net/http.Server$"
- "^net/http.Transport$"
- "^net/url.URL$"
- "^os/exec.Cmd$"
- "^reflect.StructField$"
# public libs
- "^github.com/Shopify/sarama.Config$"
- "^github.com/Shopify/sarama.ProducerMessage$"
- "^github.com/mitchellh/mapstructure.DecoderConfig$"
- "^github.com/prometheus/client_golang/.+Opts$"
- "^github.com/spf13/cobra.Command$"
- "^github.com/spf13/cobra.CompletionOptions$"
- "^github.com/stretchr/testify/mock.Mock$"
- "^github.com/testcontainers/testcontainers-go.+Request$"
- "^github.com/testcontainers/testcontainers-go.FromDockerfile$"
- "^golang.org/x/tools/go/analysis.Analyzer$"
- "^google.golang.org/protobuf/.+Options$"
- "^gopkg.in/yaml.v3.Node$"
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 100
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 50
# Ignore comments when counting lines.
# Default false
ignore-comments: true
gocognit:
# Minimal code complexity to report.
# Default: 30 (but we recommend 10-20)
min-complexity: 20
gocritic:
# Settings passed to gocritic.
# The settings key is the name of a supported gocritic checker.
# The list of supported checkers can be find in https://go-critic.github.io/overview.
settings:
captLocal:
# Whether to restrict checker to params only.
# Default: true
paramsOnly: false
underef:
# Whether to skip (*x).method() calls where x is a pointer receiver.
# Default: true
skipRecvDeref: false
gomnd:
# List of function patterns to exclude from analysis.
# Values always ignored: `time.Date`,
# `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`,
# `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`.
# Default: []
ignored-functions:
- flag.Arg
- flag.Duration.*
- flag.Float.*
- flag.Int.*
- flag.Uint.*
- os.Chmod
- os.Mkdir.*
- os.OpenFile
- os.WriteFile
- prometheus.ExponentialBuckets.*
- prometheus.LinearBuckets
gomodguard:
blocked:
# List of blocked modules.
# Default: []
modules:
- github.com/golang/protobuf:
recommendations:
- google.golang.org/protobuf
reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules"
- github.com/satori/go.uuid:
recommendations:
- github.com/google/uuid
reason: "satori's package is not maintained"
- github.com/gofrs/uuid:
recommendations:
- github.com/gofrs/uuid/v5
reason: "gofrs' package was not go module before v5"
govet:
# Enable all analyzers.
# Default: false
enable-all: true
# Disable analyzers by name.
# Run `go tool vet help` to see all analyzers.
# Default: []
disable:
- fieldalignment # too strict
# Settings per analyzer.
settings:
shadow:
# Whether to be strict about shadowing; can be noisy.
# Default: false
#strict: true
inamedparam:
# Skips check for interface methods with only a single parameter.
# Default: false
skip-single-param: true
nakedret:
# Make an issue if func has more lines of code than this setting, and it has naked returns.
# Default: 30
max-func-lines: 0
nolintlint:
# Exclude following linters from requiring an explanation.
# Default: []
allow-no-explanation: [ funlen, gocognit, lll ]
# Enable to require an explanation of nonzero length after each nolint directive.
# Default: false
require-explanation: true
# Enable to require nolint directives to mention the specific linter being suppressed.
# Default: false
require-specific: true
perfsprint:
# Optimizes into strings concatenation.
# Default: true
strconcat: false
rowserrcheck:
# database/sql is always checked
# Default: []
packages:
- github.com/jmoiron/sqlx
tenv:
# The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
# Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
# Default: false
all: true
stylecheck:
# STxxxx checks in https://staticcheck.io/docs/configuration/options/#checks
# Default: ["*"]
checks: ["all", "-ST1003"]
revive:
rules:
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter
- name: unused-parameter
disabled: true
linters:
disable-all: true
enable:
## enabled by default
#- errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases
- gosimple # specializes in simplifying a code
- govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
- ineffassign # detects when assignments to existing variables are not used
- staticcheck # is a go vet on steroids, applying a ton of static analysis checks
- typecheck # like the front-end of a Go compiler, parses and type-checks Go code
- unused # checks for unused constants, variables, functions and types
## disabled by default
- asasalint # checks for pass []any as any in variadic func(...any)
- asciicheck # checks that your code does not contain non-ASCII identifiers
- bidichk # checks for dangerous unicode character sequences
- bodyclose # checks whether HTTP response body is closed successfully
- cyclop # checks function and package cyclomatic complexity
- dupl # tool for code clone detection
- durationcheck # checks for two durations multiplied together
- errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error
#- errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13
- execinquery # checks query string in Query function which reads your Go src files and warning it finds
- exhaustive # checks exhaustiveness of enum switch statements
- exportloopref # checks for pointers to enclosing loop variables
#- forbidigo # forbids identifiers
- funlen # tool for detection of long functions
- gocheckcompilerdirectives # validates go compiler directive comments (//go:)
#- gochecknoglobals # checks that no global variables exist
- gochecknoinits # checks that no init functions are present in Go code
- gochecksumtype # checks exhaustiveness on Go "sum types"
#- gocognit # computes and checks the cognitive complexity of functions
#- goconst # finds repeated strings that could be replaced by a constant
#- gocritic # provides diagnostics that check for bugs, performance and style issues
- gocyclo # computes and checks the cyclomatic complexity of functions
- godot # checks if comments end in a period
- goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt
#- gomnd # detects magic numbers
- gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod
- gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations
- goprintffuncname # checks that printf-like functions are named with f at the end
- gosec # inspects source code for security problems
#- lll # reports long lines
- loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap)
- makezero # finds slice declarations with non-zero initial length
- mirror # reports wrong mirror patterns of bytes/strings usage
- musttag # enforces field tags in (un)marshaled structs
- nakedret # finds naked returns in functions greater than a specified function length
- nestif # reports deeply nested if statements
- nilerr # finds the code that returns nil even if it checks that the error is not nil
#- nilnil # checks that there is no simultaneous return of nil error and an invalid value
- noctx # finds sending http request without context.Context
- nolintlint # reports ill-formed or insufficient nolint directives
#- nonamedreturns # reports all named returns
- nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL
#- perfsprint # checks that fmt.Sprintf can be replaced with a faster alternative
- predeclared # finds code that shadows one of Go's predeclared identifiers
- promlinter # checks Prometheus metrics naming via promlint
- protogetter # reports direct reads from proto message fields when getters should be used
- reassign # checks that package variables are not reassigned
- revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint
- rowserrcheck # checks whether Err of rows is checked successfully
- sloglint # ensure consistent code style when using log/slog
- spancheck # checks for mistakes with OpenTelemetry/Census spans
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
- stylecheck # is a replacement for golint
- tenv # detects using os.Setenv instead of t.Setenv since Go1.17
- testableexamples # checks if examples are testable (have an expected output)
- testifylint # checks usage of github.com/stretchr/testify
#- testpackage # makes you use a separate _test package
- tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes
- unconvert # removes unnecessary type conversions
#- unparam # reports unused function parameters
- usestdlibvars # detects the possibility to use variables/constants from the Go standard library
- wastedassign # finds wasted assignment statements
- whitespace # detects leading and trailing whitespace
## you may want to enable
#- decorder # checks declaration order and count of types, constants, variables and functions
#- exhaustruct # [highly recommend to enable] checks if all structure fields are initialized
#- gci # controls golang package import order and makes it always deterministic
#- ginkgolinter # [if you use ginkgo/gomega] enforces standards of using ginkgo and gomega
#- godox # detects FIXME, TODO and other comment keywords
#- goheader # checks is file header matches to pattern
#- inamedparam # [great idea, but too strict, need to ignore a lot of cases by default] reports interfaces with unnamed method parameters
#- interfacebloat # checks the number of methods inside an interface
#- ireturn # accept interfaces, return concrete types
#- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated
#- tagalign # checks that struct tags are well aligned
#- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope
#- wrapcheck # checks that errors returned from external packages are wrapped
#- zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event
## disabled
#- containedctx # detects struct contained context.Context field
#- contextcheck # [too many false positives] checks the function whether use a non-inherited context
#- depguard # [replaced by gomodguard] checks if package imports are in a list of acceptable packages
#- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
#- dupword # [useless without config] checks for duplicate words in the source code
#- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted
#- forcetypeassert # [replaced by errcheck] finds forced type assertions
#- goerr113 # [too strict] checks the errors handling expressions
#- gofmt # [replaced by goimports] checks whether code was gofmt-ed
#- gofumpt # [replaced by goimports, gofumports is not available yet] checks whether code was gofumpt-ed
#- gosmopolitan # reports certain i18n/l10n anti-patterns in your Go codebase
#- grouper # analyzes expression groups
#- importas # enforces consistent import aliases
#- maintidx # measures the maintainability index of each function
#- misspell # [useless] finds commonly misspelled English words in comments
#- nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity
#- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test
#- tagliatelle # checks the struct tags
#- thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers
#- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines
## deprecated
#- deadcode # [deprecated, replaced by unused] finds unused code
#- exhaustivestruct # [deprecated, replaced by exhaustruct] checks if all struct's fields are initialized
#- golint # [deprecated, replaced by revive] golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
#- ifshort # [deprecated] checks that your code uses short syntax for if-statements whenever possible
#- interfacer # [deprecated] suggests narrower interface types
#- maligned # [deprecated, replaced by govet fieldalignment] detects Go structs that would take less memory if their fields were sorted
#- nosnakecase # [deprecated, replaced by revive var-naming] detects snake case of variable naming and function name
#- scopelint # [deprecated, replaced by exportloopref] checks for unpinned variables in go programs
#- structcheck # [deprecated, replaced by unused] finds unused struct fields
#- varcheck # [deprecated, replaced by unused] finds unused global variables and constants
issues:
# Maximum count of issues with the same text.
# Set to 0 to disable.
# Default: 3
max-same-issues: 50
exclude-rules:
- source: "(noinspection|TODO)"
linters: [ godot ]
- source: "//noinspection"
linters: [ gocritic ]
- path: "_test\\.go"
linters:
- bodyclose
- dupl
- funlen
- goconst
- gosec
- noctx
- wrapcheck
# TODO: remove after PR is released https://github.com/golangci/golangci-lint/pull/4386
- text: "fmt.Sprintf can be replaced with string addition"
linters: [ perfsprint ]

View File

@ -9,25 +9,37 @@ Casbin
[![Discord](https://img.shields.io/discord/1022748306096537660?logo=discord&label=discord&color=5865F2)](https://discord.gg/S5UjpzGZjN)
[![Sourcegraph](https://sourcegraph.com/github.com/casbin/casbin/-/badge.svg)](https://sourcegraph.com/github.com/casbin/casbin?badge)
[![image](https://door.casdoor.com/image.png)](https://door.casdoor.com/redirect)
**News**: still worry about how to write the correct Casbin policy? ``Casbin online editor`` is coming to help! Try it at: https://casbin.org/editor/
![casbin Logo](casbin-logo.png)
Casbin is a powerful and efficient open-source access control library for Golang projects. It provides support for enforcing authorization based on various [access control models](https://en.wikipedia.org/wiki/Computer_security_model).
<p align="center">
<sup>Sponsored by</sup>
<br>
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.casbin.org/img/stytch-white.png">
<source media="(prefers-color-scheme: light)" srcset="https://cdn.casbin.org/img/stytch-charcoal.png">
<img src="https://cdn.casbin.org/img/stytch-charcoal.png" width="275">
</picture>
</a><br/>
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin"><b>Build auth with fraud prevention, faster.</b><br/> Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.</a>
<br>
</p>
## All the languages supported by Casbin:
[![golang](https://casbin.org/img/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/img/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/img/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/img/langs/php.png)](https://github.com/php-casbin/php-casbin)
----|----|----|----
[Casbin](https://github.com/casbin/casbin) | [jCasbin](https://github.com/casbin/jcasbin) | [node-Casbin](https://github.com/casbin/node-casbin) | [PHP-Casbin](https://github.com/php-casbin/php-casbin)
production-ready | production-ready | production-ready | production-ready
| [![golang](https://casbin.org/img/langs/golang.png)](https://github.com/casbin/casbin) | [![java](https://casbin.org/img/langs/java.png)](https://github.com/casbin/jcasbin) | [![nodejs](https://casbin.org/img/langs/nodejs.png)](https://github.com/casbin/node-casbin) | [![php](https://casbin.org/img/langs/php.png)](https://github.com/php-casbin/php-casbin) |
|----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| [Casbin](https://github.com/casbin/casbin) | [jCasbin](https://github.com/casbin/jcasbin) | [node-Casbin](https://github.com/casbin/node-casbin) | [PHP-Casbin](https://github.com/php-casbin/php-casbin) |
| production-ready | production-ready | production-ready | production-ready |
[![python](https://casbin.org/img/langs/python.png)](https://github.com/casbin/pycasbin) | [![dotnet](https://casbin.org/img/langs/dotnet.png)](https://github.com/casbin-net/Casbin.NET) | [![c++](https://casbin.org/img/langs/cpp.png)](https://github.com/casbin/casbin-cpp) | [![rust](https://casbin.org/img/langs/rust.png)](https://github.com/casbin/casbin-rs)
----|----|----|----
[PyCasbin](https://github.com/casbin/pycasbin) | [Casbin.NET](https://github.com/casbin-net/Casbin.NET) | [Casbin-CPP](https://github.com/casbin/casbin-cpp) | [Casbin-RS](https://github.com/casbin/casbin-rs)
production-ready | production-ready | beta-test | production-ready
| [![python](https://casbin.org/img/langs/python.png)](https://github.com/casbin/pycasbin) | [![dotnet](https://casbin.org/img/langs/dotnet.png)](https://github.com/casbin-net/Casbin.NET) | [![c++](https://casbin.org/img/langs/cpp.png)](https://github.com/casbin/casbin-cpp) | [![rust](https://casbin.org/img/langs/rust.png)](https://github.com/casbin/casbin-rs) |
|------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| [PyCasbin](https://github.com/casbin/pycasbin) | [Casbin.NET](https://github.com/casbin-net/Casbin.NET) | [Casbin-CPP](https://github.com/casbin/casbin-cpp) | [Casbin-RS](https://github.com/casbin/casbin-rs) |
| production-ready | production-ready | production-ready | production-ready |
## Table of contents
@ -118,7 +130,7 @@ m = r.obj == p.obj && r.act == p.act || r.obj in ('data2', 'data3')
But you **SHOULD** make sure that the length of the array is **MORE** than **1**, otherwise there will cause it to panic.
For more operators, you may take a look at [govaluate](https://github.com/Knetic/govaluate)
For more operators, you may take a look at [govaluate](https://github.com/casbin/govaluate)
## Features
@ -216,19 +228,19 @@ https://casbin.org/docs/benchmark
## Examples
Model | Model file | Policy file
----|------|----
ACL | [basic_model.conf](https://github.com/casbin/casbin/blob/master/examples/basic_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv)
ACL with superuser | [basic_model_with_root.conf](https://github.com/casbin/casbin/blob/master/examples/basic_with_root_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv)
ACL without users | [basic_model_without_users.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_model.conf) | [basic_policy_without_users.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_policy.csv)
ACL without resources | [basic_model_without_resources.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_model.conf) | [basic_policy_without_resources.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_policy.csv)
RBAC | [rbac_model.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_model.conf) | [rbac_policy.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_policy.csv)
RBAC with resource roles | [rbac_model_with_resource_roles.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_model.conf) | [rbac_policy_with_resource_roles.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_policy.csv)
RBAC with domains/tenants | [rbac_model_with_domains.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_model.conf) | [rbac_policy_with_domains.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_policy.csv)
ABAC | [abac_model.conf](https://github.com/casbin/casbin/blob/master/examples/abac_model.conf) | N/A
RESTful | [keymatch_model.conf](https://github.com/casbin/casbin/blob/master/examples/keymatch_model.conf) | [keymatch_policy.csv](https://github.com/casbin/casbin/blob/master/examples/keymatch_policy.csv)
Deny-override | [rbac_model_with_deny.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_policy.csv)
Priority | [priority_model.conf](https://github.com/casbin/casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/casbin/casbin/blob/master/examples/priority_policy.csv)
| Model | Model file | Policy file |
|---------------------------|----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
| ACL | [basic_model.conf](https://github.com/casbin/casbin/blob/master/examples/basic_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv) |
| ACL with superuser | [basic_model_with_root.conf](https://github.com/casbin/casbin/blob/master/examples/basic_with_root_model.conf) | [basic_policy.csv](https://github.com/casbin/casbin/blob/master/examples/basic_policy.csv) |
| ACL without users | [basic_model_without_users.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_model.conf) | [basic_policy_without_users.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_users_policy.csv) |
| ACL without resources | [basic_model_without_resources.conf](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_model.conf) | [basic_policy_without_resources.csv](https://github.com/casbin/casbin/blob/master/examples/basic_without_resources_policy.csv) |
| RBAC | [rbac_model.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_model.conf) | [rbac_policy.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_policy.csv) |
| RBAC with resource roles | [rbac_model_with_resource_roles.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_model.conf) | [rbac_policy_with_resource_roles.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_resource_roles_policy.csv) |
| RBAC with domains/tenants | [rbac_model_with_domains.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_model.conf) | [rbac_policy_with_domains.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_domains_policy.csv) |
| ABAC | [abac_model.conf](https://github.com/casbin/casbin/blob/master/examples/abac_model.conf) | N/A |
| RESTful | [keymatch_model.conf](https://github.com/casbin/casbin/blob/master/examples/keymatch_model.conf) | [keymatch_policy.csv](https://github.com/casbin/casbin/blob/master/examples/keymatch_policy.csv) |
| Deny-override | [rbac_model_with_deny.conf](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_model.conf) | [rbac_policy_with_deny.csv](https://github.com/casbin/casbin/blob/master/examples/rbac_with_deny_policy.csv) |
| Priority | [priority_model.conf](https://github.com/casbin/casbin/blob/master/examples/priority_model.conf) | [priority_policy.csv](https://github.com/casbin/casbin/blob/master/examples/priority_policy.csv) |
## Middlewares

View File

@ -26,17 +26,17 @@ import (
)
var (
// DEFAULT_SECTION specifies the name of a section if no name provided
// DEFAULT_SECTION specifies the name of a section if no name provided.
DEFAULT_SECTION = "default"
// DEFAULT_COMMENT defines what character(s) indicate a comment `#`
// DEFAULT_COMMENT defines what character(s) indicate a comment `#`.
DEFAULT_COMMENT = []byte{'#'}
// DEFAULT_COMMENT_SEM defines what alternate character(s) indicate a comment `;`
// DEFAULT_COMMENT_SEM defines what alternate character(s) indicate a comment `;`.
DEFAULT_COMMENT_SEM = []byte{';'}
// DEFAULT_MULTI_LINE_SEPARATOR defines what character indicates a multi-line content
// DEFAULT_MULTI_LINE_SEPARATOR defines what character indicates a multi-line content.
DEFAULT_MULTI_LINE_SEPARATOR = []byte{'\\'}
)
// ConfigInterface defines the behavior of a Config implementation
// ConfigInterface defines the behavior of a Config implementation.
type ConfigInterface interface {
String(key string) string
Strings(key string) []string
@ -47,7 +47,7 @@ type ConfigInterface interface {
Set(key string, value string) error
}
// Config represents an implementation of the ConfigInterface
// Config represents an implementation of the ConfigInterface.
type Config struct {
// Section:key=value
data map[string]map[string]string
@ -116,7 +116,7 @@ func (c *Config) parseBuffer(buf *bufio.Reader) error {
if err == io.EOF {
// force write when buffer is not flushed yet
if buffer.Len() > 0 {
if err := c.write(section, lineNum, &buffer); err != nil {
if err = c.write(section, lineNum, &buffer); err != nil {
return err
}
}
@ -185,33 +185,33 @@ func (c *Config) write(section string, lineNum int, b *bytes.Buffer) error {
return nil
}
// Bool lookups up the value using the provided key and converts the value to a bool
// Bool lookups up the value using the provided key and converts the value to a bool.
func (c *Config) Bool(key string) (bool, error) {
return strconv.ParseBool(c.get(key))
}
// Int lookups up the value using the provided key and converts the value to a int
// Int lookups up the value using the provided key and converts the value to a int.
func (c *Config) Int(key string) (int, error) {
return strconv.Atoi(c.get(key))
}
// Int64 lookups up the value using the provided key and converts the value to a int64
// Int64 lookups up the value using the provided key and converts the value to a int64.
func (c *Config) Int64(key string) (int64, error) {
return strconv.ParseInt(c.get(key), 10, 64)
}
// Float64 lookups up the value using the provided key and converts the value to a float64
// Float64 lookups up the value using the provided key and converts the value to a float64.
func (c *Config) Float64(key string) (float64, error) {
return strconv.ParseFloat(c.get(key), 64)
}
// String lookups up the value using the provided key and converts the value to a string
// String lookups up the value using the provided key and converts the value to a string.
func (c *Config) String(key string) string {
return c.get(key)
}
// Strings lookups up the value using the provided key and converts the value to an array of string
// by splitting the string by comma
// by splitting the string by comma.
func (c *Config) Strings(key string) []string {
v := c.get(key)
if v == "" {
@ -220,7 +220,7 @@ func (c *Config) Strings(key string) []string {
return strings.Split(v, ",")
}
// Set sets the value for the specific key in the Config
// Set sets the value for the specific key in the Config.
func (c *Config) Set(key string, value string) error {
if len(key) == 0 {
return errors.New("key is empty")
@ -243,7 +243,7 @@ func (c *Config) Set(key string, value string) error {
return nil
}
// section.key or key
// section.key or key.
func (c *Config) get(key string) string {
var (
section string

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package effector
package effector //nolint:cyclop // TODO
// Effect is the result for a policy rule.
type Effect int

View File

@ -17,7 +17,6 @@ package casbin
import (
"errors"
"fmt"
"regexp"
"runtime/debug"
"strings"
"sync"
@ -31,8 +30,7 @@ import (
defaultrolemanager "github.com/casbin/casbin/v2/rbac/default-role-manager"
"github.com/casbin/casbin/v2/util"
"github.com/Knetic/govaluate"
"github.com/tidwall/gjson"
"github.com/casbin/govaluate"
)
// Enforcer is the main interface for authorization enforcement and policy management.
@ -59,7 +57,7 @@ type Enforcer struct {
logger log.Logger
}
// EnforceContext is used as the first element of the parameter "rvals" in method "enforce"
// EnforceContext is used as the first element of the parameter "rvals" in method "enforce".
type EnforceContext struct {
RType string
PType string
@ -102,7 +100,8 @@ func NewEnforcer(params ...interface{}) (*Enforcer, error) {
}
}
if paramLen-parsedParamLen == 2 {
switch paramLen - parsedParamLen {
case 2:
switch p0 := params[0].(type) {
case string:
switch p1 := params[1].(type) {
@ -128,7 +127,7 @@ func NewEnforcer(params ...interface{}) (*Enforcer, error) {
}
}
}
} else if paramLen-parsedParamLen == 1 {
case 1:
switch p0 := params[0].(type) {
case string:
err := e.InitWithFile(p0, "")
@ -141,9 +140,9 @@ func NewEnforcer(params ...interface{}) (*Enforcer, error) {
return nil, err
}
}
} else if paramLen-parsedParamLen == 0 {
case 0:
return e, nil
} else {
default:
return nil, errors.New("invalid parameters for enforcer")
}
@ -202,6 +201,9 @@ func (e *Enforcer) SetLogger(logger log.Logger) {
for k := range e.rmMap {
e.rmMap[k].SetLogger(e.logger)
}
for k := range e.condRmMap {
e.condRmMap[k].SetLogger(e.logger)
}
}
func (e *Enforcer) initialize() {
@ -275,12 +277,20 @@ func (e *Enforcer) SetWatcher(watcher persist.Watcher) error {
// GetRoleManager gets the current role manager.
func (e *Enforcer) GetRoleManager() rbac.RoleManager {
return e.rmMap["g"]
if e.rmMap != nil && e.rmMap["g"] != nil {
return e.rmMap["g"]
} else {
return nil
}
}
// GetNamedRoleManager gets the role manager for the named policy.
func (e *Enforcer) GetNamedRoleManager(ptype string) rbac.RoleManager {
return e.rmMap[ptype]
if e.rmMap != nil && e.rmMap[ptype] != nil {
return e.rmMap[ptype]
} else {
return nil
}
}
// SetRoleManager sets the current role manager.
@ -313,8 +323,6 @@ func (e *Enforcer) ClearPolicy() {
// LoadPolicy reloads the policy from file/database.
func (e *Enforcer) LoadPolicy() error {
e.invalidateMatcherMap()
needToRebuild := false
newModel := e.model.Copy()
newModel.ClearPolicy()
@ -351,6 +359,7 @@ func (e *Enforcer) LoadPolicy() error {
}
}
e.model = newModel
e.invalidateMatcherMap()
return nil
}
@ -530,13 +539,16 @@ func (e *Enforcer) EnableAutoBuildRoleLinks(autoBuildRoleLinks bool) {
e.autoBuildRoleLinks = autoBuildRoleLinks
}
// EnableAcceptJsonRequest controls whether to accept json as a request parameter
// EnableAcceptJsonRequest controls whether to accept json as a request parameter.
func (e *Enforcer) EnableAcceptJsonRequest(acceptJsonRequest bool) {
e.acceptJsonRequest = acceptJsonRequest
}
// BuildRoleLinks manually rebuild the role inheritance relations.
func (e *Enforcer) BuildRoleLinks() error {
if e.rmMap == nil {
return errors.New("rmMap is nil")
}
for _, rm := range e.rmMap {
err := rm.Clear()
if err != nil {
@ -559,7 +571,7 @@ func (e *Enforcer) BuildIncrementalConditionalRoleLinks(op model.PolicyOp, ptype
return e.model.BuildIncrementalConditionalRoleLinks(e.condRmMap, op, "g", ptype, rules)
}
// NewEnforceContext Create a default structure based on the suffix
// NewEnforceContext Create a default structure based on the suffix.
func NewEnforceContext(suffix string) EnforceContext {
return EnforceContext{
RType: "r" + suffix,
@ -574,7 +586,7 @@ func (e *Enforcer) invalidateMatcherMap() {
}
// enforce use a custom matcher to decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (matcher, sub, obj, act), use model matcher by default when matcher is "".
func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interface{}) (ok bool, err error) {
func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interface{}) (ok bool, err error) { //nolint:funlen,cyclop,gocyclo // TODO: reduce function complexity
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v\n%s", r, debug.Stack())
@ -637,7 +649,18 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
}
if e.acceptJsonRequest {
expString = requestJsonReplace(expString, rTokens, rvals)
// try to parse all request values from json to map[string]interface{}
// skip if there is an error
for i, rval := range rvals {
switch rval := rval.(type) {
case string:
var mapValue map[string]interface{}
mapValue, err = util.JsonToMap(rval)
if err == nil {
rvals[i] = mapValue
}
}
}
}
parameters := enforceParameters{
@ -671,7 +694,7 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
var effect effector.Effect
var explainIndex int
if policyLen := len(e.model["p"][pType].Policy); policyLen != 0 && strings.Contains(expString, pType+"_") {
if policyLen := len(e.model["p"][pType].Policy); policyLen != 0 && strings.Contains(expString, pType+"_") { //nolint:nestif // TODO: reduce function complexity
policyEffects = make([]effector.Effect, policyLen)
matcherResults = make([]float64, policyLen)
@ -685,16 +708,7 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
pvals)
}
if e.acceptJsonRequest {
pvalsCopy := make([]string, len(pvals))
copy(pvalsCopy, pvals)
for i, pStr := range pvalsCopy {
pvalsCopy[i] = requestJsonReplace(util.EscapeAssertion(pStr), rTokens, rvals)
}
parameters.pVals = pvalsCopy
} else {
parameters.pVals = pvals
}
parameters.pVals = pvals
result, err := expression.Eval(parameters)
// log.LogPrint("Result: ", result)
@ -744,7 +758,6 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
}
}
} else {
if hasEval && len(e.model["p"][pType].Policy) == 0 {
return false, errors.New("please make sure rule exists in policy when using eval() in matcher")
}
@ -796,31 +809,6 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
return result, nil
}
var requestObjectRegex = regexp.MustCompile(`r[_.][A-Za-z_0-9]+\.[A-Za-z_0-9.]+[A-Za-z_0-9]`)
var requestObjectRegexPrefix = regexp.MustCompile(`r[_.][A-Za-z_0-9]+\.`)
// requestJsonReplace used to support request parameters of type json
// It will replace the access of the request object in matchers or policy with the actual value in the request json parameter
// For example: request sub = `{"Owner": "alice", "Age": 30}`
// policy: p, r.sub.Age > 18, /data1, read ==> p, 30 > 18, /data1, read
// matchers: m = r.sub == r.obj.Owner ==> m = r.sub == "alice"
func requestJsonReplace(str string, rTokens map[string]int, rvals []interface{}) string {
matches := requestObjectRegex.FindAllString(str, -1)
for _, matchesStr := range matches {
prefix := requestObjectRegexPrefix.FindString(matchesStr)
jsonPath := strings.TrimPrefix(matchesStr, prefix)
tokenIndex := rTokens[prefix[:len(prefix)-1]]
if jsonStr, ok := rvals[tokenIndex].(string); ok {
newStr := gjson.Get(jsonStr, jsonPath).String()
if !util.IsNumeric(newStr) {
newStr = `"` + newStr + `"`
}
str = strings.Replace(str, matchesStr, newStr, -1)
}
}
return str
}
func (e *Enforcer) getAndStoreMatcherExpression(hasEval bool, expString string, functions map[string]govaluate.ExpressionFunction) (*govaluate.EvaluableExpression, error) {
var expression *govaluate.EvaluableExpression
var err error
@ -848,21 +836,21 @@ func (e *Enforcer) EnforceWithMatcher(matcher string, rvals ...interface{}) (boo
return e.enforce(matcher, nil, rvals...)
}
// EnforceEx explain enforcement by informing matched rules
// EnforceEx explain enforcement by informing matched rules.
func (e *Enforcer) EnforceEx(rvals ...interface{}) (bool, []string, error) {
explain := []string{}
result, err := e.enforce("", &explain, rvals...)
return result, explain, err
}
// EnforceExWithMatcher use a custom matcher and explain enforcement by informing matched rules
// EnforceExWithMatcher use a custom matcher and explain enforcement by informing matched rules.
func (e *Enforcer) EnforceExWithMatcher(matcher string, rvals ...interface{}) (bool, []string, error) {
explain := []string{}
result, err := e.enforce(matcher, &explain, rvals...)
return result, explain, err
}
// BatchEnforce enforce in batches
// BatchEnforce enforce in batches.
func (e *Enforcer) BatchEnforce(requests [][]interface{}) ([]bool, error) {
var results []bool
for _, request := range requests {
@ -875,7 +863,7 @@ func (e *Enforcer) BatchEnforce(requests [][]interface{}) ([]bool, error) {
return results, nil
}
// BatchEnforceWithMatcher enforce with matcher in batches
// BatchEnforceWithMatcher enforce with matcher in batches.
func (e *Enforcer) BatchEnforceWithMatcher(matcher string, requests [][]interface{}) ([]bool, error) {
var results []bool
for _, request := range requests {
@ -888,7 +876,7 @@ func (e *Enforcer) BatchEnforceWithMatcher(matcher string, requests [][]interfac
return results, nil
}
// AddNamedMatchingFunc add MatchingFunc by ptype RoleManager
// AddNamedMatchingFunc add MatchingFunc by ptype RoleManager.
func (e *Enforcer) AddNamedMatchingFunc(ptype, name string, fn rbac.MatchingFunc) bool {
if rm, ok := e.rmMap[ptype]; ok {
rm.AddMatchingFunc(name, fn)
@ -897,7 +885,7 @@ func (e *Enforcer) AddNamedMatchingFunc(ptype, name string, fn rbac.MatchingFunc
return false
}
// AddNamedDomainMatchingFunc add MatchingFunc by ptype to RoleManager
// AddNamedDomainMatchingFunc add MatchingFunc by ptype to RoleManager.
func (e *Enforcer) AddNamedDomainMatchingFunc(ptype, name string, fn rbac.MatchingFunc) bool {
if rm, ok := e.rmMap[ptype]; ok {
rm.AddDomainMatchingFunc(name, fn)
@ -907,7 +895,7 @@ func (e *Enforcer) AddNamedDomainMatchingFunc(ptype, name string, fn rbac.Matchi
}
// AddNamedLinkConditionFunc Add condition function fn for Link userName->roleName,
// when fn returns true, Link is valid, otherwise invalid
// when fn returns true, Link is valid, otherwise invalid.
func (e *Enforcer) AddNamedLinkConditionFunc(ptype, user, role string, fn rbac.LinkConditionFunc) bool {
if rm, ok := e.condRmMap[ptype]; ok {
rm.AddLinkConditionFunc(user, role, fn)
@ -917,7 +905,7 @@ func (e *Enforcer) AddNamedLinkConditionFunc(ptype, user, role string, fn rbac.L
}
// AddNamedDomainLinkConditionFunc Add condition function fn for Link userName-> {roleName, domain},
// when fn returns true, Link is valid, otherwise invalid
// when fn returns true, Link is valid, otherwise invalid.
func (e *Enforcer) AddNamedDomainLinkConditionFunc(ptype, user, role string, domain string, fn rbac.LinkConditionFunc) bool {
if rm, ok := e.condRmMap[ptype]; ok {
rm.AddDomainLinkConditionFunc(user, role, domain, fn)
@ -926,7 +914,7 @@ func (e *Enforcer) AddNamedDomainLinkConditionFunc(ptype, user, role string, dom
return false
}
// SetNamedLinkConditionFuncParams Sets the parameters of the condition function fn for Link userName->roleName
// SetNamedLinkConditionFuncParams Sets the parameters of the condition function fn for Link userName->roleName.
func (e *Enforcer) SetNamedLinkConditionFuncParams(ptype, user, role string, params ...string) bool {
if rm, ok := e.condRmMap[ptype]; ok {
rm.SetLinkConditionFuncParams(user, role, params...)
@ -936,7 +924,7 @@ func (e *Enforcer) SetNamedLinkConditionFuncParams(ptype, user, role string, par
}
// SetNamedDomainLinkConditionFuncParams Sets the parameters of the condition function fn
// for Link userName->{roleName, domain}
// for Link userName->{roleName, domain}.
func (e *Enforcer) SetNamedDomainLinkConditionFuncParams(ptype, user, role, domain string, params ...string) bool {
if rm, ok := e.condRmMap[ptype]; ok {
rm.SetDomainLinkConditionFuncParams(user, role, domain, params...)
@ -945,7 +933,7 @@ func (e *Enforcer) SetNamedDomainLinkConditionFuncParams(ptype, user, role, doma
return false
}
// assumes bounds have already been checked
// assumes bounds have already been checked.
type enforceParameters struct {
rTokens map[string]int
rVals []interface{}
@ -954,7 +942,7 @@ type enforceParameters struct {
pVals []string
}
// implements govaluate.Parameters
// implements govaluate.Parameters.
func (p enforceParameters) Get(name string) (interface{}, error) {
if name == "" {
return nil, nil

View File

@ -23,7 +23,7 @@ import (
"github.com/casbin/casbin/v2/persist/cache"
)
// CachedEnforcer wraps Enforcer and provides decision cache
// CachedEnforcer wraps Enforcer and provides decision cache.
type CachedEnforcer struct {
*Enforcer
expireTime time.Duration
@ -61,7 +61,7 @@ func (e *CachedEnforcer) EnableCache(enableCache bool) {
}
// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
// if rvals is not string , ingore the cache
// if rvals is not string , ignore the cache.
func (e *CachedEnforcer) Enforce(rvals ...interface{}) (bool, error) {
if atomic.LoadInt32(&e.enableCache) == 0 {
return e.Enforcer.Enforce(rvals...)
@ -127,8 +127,8 @@ func (e *CachedEnforcer) RemovePolicies(rules [][]string) (bool, error) {
}
func (e *CachedEnforcer) getCachedResult(key string) (res bool, err error) {
e.locker.RLock()
defer e.locker.RUnlock()
e.locker.Lock()
defer e.locker.Unlock()
return e.cache.Get(key)
}

View File

@ -22,7 +22,7 @@ import (
"github.com/casbin/casbin/v2/persist/cache"
)
// SyncedCachedEnforcer wraps Enforcer and provides decision sync cache
// SyncedCachedEnforcer wraps Enforcer and provides decision sync cache.
type SyncedCachedEnforcer struct {
*SyncedEnforcer
expireTime time.Duration
@ -56,7 +56,7 @@ func (e *SyncedCachedEnforcer) EnableCache(enableCache bool) {
}
// Enforce decides whether a "subject" can access a "object" with the operation "action", input parameters are usually: (sub, obj, act).
// if rvals is not string , ingore the cache
// if rvals is not string , ignore the cache.
func (e *SyncedCachedEnforcer) Enforce(rvals ...interface{}) (bool, error) {
if atomic.LoadInt32(&e.enableCache) == 0 {
return e.SyncedEnforcer.Enforce(rvals...)
@ -129,7 +129,7 @@ func (e *SyncedCachedEnforcer) SetExpireTime(expireTime time.Duration) {
e.expireTime = expireTime
}
// SetCache need to be sync cache
// SetCache need to be sync cache.
func (e *SyncedCachedEnforcer) SetCache(c cache.Cache) {
e.locker.Lock()
defer e.locker.Unlock()

View File

@ -22,8 +22,8 @@ func NewDistributedEnforcer(params ...interface{}) (*DistributedEnforcer, error)
}
// SetDispatcher sets the current dispatcher.
func (e *DistributedEnforcer) SetDispatcher(dispatcher persist.Dispatcher) {
e.dispatcher = dispatcher
func (d *DistributedEnforcer) SetDispatcher(dispatcher persist.Dispatcher) {
d.dispatcher = dispatcher
}
// AddPoliciesSelf provides a method for dispatcher to add authorization rules to the current policy.
@ -64,7 +64,7 @@ func (d *DistributedEnforcer) RemovePoliciesSelf(shouldPersist func() bool, sec
d.m.Lock()
defer d.m.Unlock()
if shouldPersist != nil && shouldPersist() {
if err := d.adapter.(persist.BatchAdapter).RemovePolicies(sec, ptype, rules); err != nil {
if err = d.adapter.(persist.BatchAdapter).RemovePolicies(sec, ptype, rules); err != nil {
if err.Error() != notImplemented {
return nil, err
}
@ -74,7 +74,7 @@ func (d *DistributedEnforcer) RemovePoliciesSelf(shouldPersist func() bool, sec
affected = d.model.RemovePoliciesWithAffected(sec, ptype, rules)
if sec == "g" {
err := d.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, affected)
err = d.BuildIncrementalRoleLinks(model.PolicyRemove, ptype, affected)
if err != nil {
return affected, err
}

View File

@ -15,18 +15,18 @@
package casbin
import (
"github.com/Knetic/govaluate"
"github.com/casbin/casbin/v2/effector"
"github.com/casbin/casbin/v2/model"
"github.com/casbin/casbin/v2/persist"
"github.com/casbin/casbin/v2/rbac"
"github.com/casbin/govaluate"
)
var _ IEnforcer = &Enforcer{}
var _ IEnforcer = &SyncedEnforcer{}
var _ IEnforcer = &CachedEnforcer{}
// IEnforcer is the API interface of Enforcer
// IEnforcer is the API interface of Enforcer.
type IEnforcer interface {
/* Enforcer API */
InitWithFile(modelPath string, policyPath string) error
@ -69,7 +69,7 @@ type IEnforcer interface {
AddPermissionsForUser(user string, permissions ...[]string) (bool, error)
DeletePermissionForUser(user string, permission ...string) (bool, error)
DeletePermissionsForUser(user string) (bool, error)
GetPermissionsForUser(user string, domain ...string) [][]string
GetPermissionsForUser(user string, domain ...string) ([][]string, error)
HasPermissionForUser(user string, permission ...string) bool
GetImplicitRolesForUser(name string, domain ...string) ([]string, error)
GetImplicitPermissionsForUser(user string, domain ...string) ([][]string, error)
@ -86,6 +86,12 @@ type IEnforcer interface {
GetPermissionsForUserInDomain(user string, domain string) [][]string
AddRoleForUserInDomain(user string, role string, domain string) (bool, error)
DeleteRoleForUserInDomain(user string, role string, domain string) (bool, error)
GetAllUsersByDomain(domain string) []string
DeleteRolesForUserInDomain(user string, domain string) (bool, error)
DeleteAllUsersByDomain(domain string) (bool, error)
DeleteDomains(domains ...string) (bool, error)
GetAllDomains() ([]string, error)
GetAllRolesByDomain(domain string) []string
/* Management API */
GetAllSubjects() []string

View File

@ -19,14 +19,14 @@ import (
"sync/atomic"
"time"
"github.com/Knetic/govaluate"
"github.com/casbin/govaluate"
"github.com/casbin/casbin/v2/persist"
"github.com/casbin/casbin/v2/rbac"
defaultrolemanager "github.com/casbin/casbin/v2/rbac/default-role-manager"
)
// SyncedEnforcer wraps Enforcer and provides synchronized access
// SyncedEnforcer wraps Enforcer and provides synchronized access.
type SyncedEnforcer struct {
*Enforcer
m sync.RWMutex
@ -48,17 +48,17 @@ func NewSyncedEnforcer(params ...interface{}) (*SyncedEnforcer, error) {
return e, nil
}
// GetLock return the private RWMutex lock
// GetLock return the private RWMutex lock.
func (e *SyncedEnforcer) GetLock() *sync.RWMutex {
return &e.m
}
// IsAutoLoadingRunning check if SyncedEnforcer is auto loading policies
// IsAutoLoadingRunning check if SyncedEnforcer is auto loading policies.
func (e *SyncedEnforcer) IsAutoLoadingRunning() bool {
return atomic.LoadInt32(&(e.autoLoadRunning)) != 0
}
// StartAutoLoadPolicy starts a go routine that will every specified duration call LoadPolicy
// StartAutoLoadPolicy starts a go routine that will every specified duration call LoadPolicy.
func (e *SyncedEnforcer) StartAutoLoadPolicy(d time.Duration) {
// Don't start another goroutine if there is already one running
if !atomic.CompareAndSwapInt32(&e.autoLoadRunning, 0, 1) {
@ -204,28 +204,28 @@ func (e *SyncedEnforcer) EnforceWithMatcher(matcher string, rvals ...interface{}
return e.Enforcer.EnforceWithMatcher(matcher, rvals...)
}
// EnforceEx explain enforcement by informing matched rules
// EnforceEx explain enforcement by informing matched rules.
func (e *SyncedEnforcer) EnforceEx(rvals ...interface{}) (bool, []string, error) {
e.m.RLock()
defer e.m.RUnlock()
return e.Enforcer.EnforceEx(rvals...)
}
// EnforceExWithMatcher use a custom matcher and explain enforcement by informing matched rules
// EnforceExWithMatcher use a custom matcher and explain enforcement by informing matched rules.
func (e *SyncedEnforcer) EnforceExWithMatcher(matcher string, rvals ...interface{}) (bool, []string, error) {
e.m.RLock()
defer e.m.RUnlock()
return e.Enforcer.EnforceExWithMatcher(matcher, rvals...)
}
// BatchEnforce enforce in batches
// BatchEnforce enforce in batches.
func (e *SyncedEnforcer) BatchEnforce(requests [][]interface{}) ([]bool, error) {
e.m.RLock()
defer e.m.RUnlock()
return e.Enforcer.BatchEnforce(requests)
}
// BatchEnforceWithMatcher enforce with matcher in batches
// BatchEnforceWithMatcher enforce with matcher in batches.
func (e *SyncedEnforcer) BatchEnforceWithMatcher(matcher string, requests [][]interface{}) ([]bool, error) {
e.m.RLock()
defer e.m.RUnlock()
@ -378,7 +378,7 @@ func (e *SyncedEnforcer) AddPolicies(rules [][]string) (bool, error) {
// AddPoliciesEx adds authorization rules to the current policy.
// If the rule already exists, the rule will not be added.
// But unlike AddPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddPolicies, other non-existent rules are added instead of returning false directly.
func (e *SyncedEnforcer) AddPoliciesEx(rules [][]string) (bool, error) {
e.m.Lock()
defer e.m.Unlock()
@ -405,7 +405,7 @@ func (e *SyncedEnforcer) AddNamedPolicies(ptype string, rules [][]string) (bool,
// AddNamedPoliciesEx adds authorization rules to the current named policy.
// If the rule already exists, the rule will not be added.
// But unlike AddNamedPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddNamedPolicies, other non-existent rules are added instead of returning false directly.
func (e *SyncedEnforcer) AddNamedPoliciesEx(ptype string, rules [][]string) (bool, error) {
e.m.Lock()
defer e.m.Unlock()
@ -526,7 +526,7 @@ func (e *SyncedEnforcer) AddGroupingPolicies(rules [][]string) (bool, error) {
// AddGroupingPoliciesEx adds role inheritance rules to the current policy.
// If the rule already exists, the rule will not be added.
// But unlike AddGroupingPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddGroupingPolicies, other non-existent rules are added instead of returning false directly.
func (e *SyncedEnforcer) AddGroupingPoliciesEx(rules [][]string) (bool, error) {
e.m.Lock()
defer e.m.Unlock()
@ -553,7 +553,7 @@ func (e *SyncedEnforcer) AddNamedGroupingPolicies(ptype string, rules [][]string
// AddNamedGroupingPoliciesEx adds named role inheritance rules to the current policy.
// If the rule already exists, the rule will not be added.
// But unlike AddNamedGroupingPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddNamedGroupingPolicies, other non-existent rules are added instead of returning false directly.
func (e *SyncedEnforcer) AddNamedGroupingPoliciesEx(ptype string, rules [][]string) (bool, error) {
e.m.Lock()
defer e.m.Unlock()

View File

@ -16,7 +16,7 @@ package errors
import "errors"
// Global errors for rbac defined here
// Global errors for rbac defined here.
var (
ErrNameNotFound = errors.New("error: name does not exist")
ErrDomainParameter = errors.New("error: domain should be 1 parameter")
@ -24,7 +24,7 @@ var (
ErrUseDomainParameter = errors.New("error: useDomain should be 1 parameter")
ErrInvalidFieldValuesParameter = errors.New("fieldValues requires at least one parameter")
// GetAllowedObjectConditions errors
// GetAllowedObjectConditions errors.
ErrObjCondition = errors.New("need to meet the prefix required by the object condition")
ErrEmptyCondition = errors.New("GetAllowedObjectConditions have an empty condition")
)

View File

@ -66,7 +66,7 @@ func (e *Enforcer) addPolicyWithoutNotify(sec string, ptype string, rule []strin
// addPoliciesWithoutNotify adds rules to the current policy without notify
// If autoRemoveRepeat == true, existing rules are automatically filtered
// Otherwise, false is returned directly
// Otherwise, false is returned directly.
func (e *Enforcer) addPoliciesWithoutNotify(sec string, ptype string, rules [][]string, autoRemoveRepeat bool) (bool, error) {
if e.dispatcher != nil && e.autoNotifyDispatcher {
return true, e.dispatcher.AddPolicies(sec, ptype, rules)
@ -329,7 +329,7 @@ func (e *Enforcer) addPolicy(sec string, ptype string, rule []string) (bool, err
// addPolicies adds rules to the current policy.
// If autoRemoveRepeat == true, existing rules are automatically filtered
// Otherwise, false is returned directly
// Otherwise, false is returned directly.
func (e *Enforcer) addPolicies(sec string, ptype string, rules [][]string, autoRemoveRepeat bool) (bool, error) {
ok, err := e.addPoliciesWithoutNotify(sec, ptype, rules, autoRemoveRepeat)
if !ok || err != nil {
@ -364,7 +364,6 @@ func (e *Enforcer) removePolicy(sec string, ptype string, rule []string) (bool,
err = e.watcher.Update()
}
return true, err
}
return true, nil

View File

@ -46,7 +46,7 @@ func LogPolicy(policy map[string][][]string) {
logger.LogPolicy(policy)
}
// LogError logs the error information
// LogError logs the error information.
func LogError(err error, msg ...string) {
logger.LogError(err, msg...)
}

View File

@ -19,8 +19,8 @@ import (
"fmt"
"strings"
"github.com/Knetic/govaluate"
"github.com/casbin/casbin/v2/util"
"github.com/casbin/govaluate"
)
// GetAllSubjects gets the list of subjects that show up in the current policy.
@ -111,8 +111,15 @@ func (e *Enforcer) GetFilteredNamedPolicyWithMatcher(ptype string, matcher strin
functions := e.fm.GetFunctions()
if _, ok := e.model["g"]; ok {
for key, ast := range e.model["g"] {
rm := ast.RM
functions[key] = util.GenerateGFunction(rm)
// g must be a normal role definition (ast.RM != nil)
// or a conditional role definition (ast.CondRM != nil)
// ast.RM and ast.CondRM shouldn't be nil at the same time
if ast.RM != nil {
functions[key] = util.GenerateGFunction(ast.RM)
}
if ast.CondRM != nil {
functions[key] = util.GenerateConditionalGFunction(ast.CondRM)
}
}
}
@ -209,7 +216,7 @@ func (e *Enforcer) AddPolicies(rules [][]string) (bool, error) {
// AddPoliciesEx adds authorization rules to the current policy.
// If the rule already exists, the rule will not be added.
// But unlike AddPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddPolicies, other non-existent rules are added instead of returning false directly.
func (e *Enforcer) AddPoliciesEx(rules [][]string) (bool, error) {
return e.AddNamedPoliciesEx("p", rules)
}
@ -239,7 +246,7 @@ func (e *Enforcer) AddNamedPolicies(ptype string, rules [][]string) (bool, error
// AddNamedPoliciesEx adds authorization rules to the current named policy.
// If the rule already exists, the rule will not be added.
// But unlike AddNamedPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddNamedPolicies, other non-existent rules are added instead of returning false directly.
func (e *Enforcer) AddNamedPoliciesEx(ptype string, rules [][]string) (bool, error) {
return e.addPolicies("p", ptype, rules, true)
}
@ -343,7 +350,7 @@ func (e *Enforcer) AddGroupingPolicies(rules [][]string) (bool, error) {
// AddGroupingPoliciesEx adds role inheritance rules to the current policy.
// If the rule already exists, the rule will not be added.
// But unlike AddGroupingPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddGroupingPolicies, other non-existent rules are added instead of returning false directly.
func (e *Enforcer) AddGroupingPoliciesEx(rules [][]string) (bool, error) {
return e.AddNamedGroupingPoliciesEx("g", rules)
}
@ -377,7 +384,7 @@ func (e *Enforcer) AddNamedGroupingPolicies(ptype string, rules [][]string) (boo
// AddNamedGroupingPoliciesEx adds named role inheritance rules to the current policy.
// If the rule already exists, the rule will not be added.
// But unlike AddNamedGroupingPolicies, other non-existent rules are added instead of returning false directly
// But unlike AddNamedGroupingPolicies, other non-existent rules are added instead of returning false directly.
func (e *Enforcer) AddNamedGroupingPoliciesEx(ptype string, rules [][]string) (bool, error) {
return e.addPolicies("g", ptype, rules, true)
}

View File

@ -23,7 +23,7 @@ import (
)
// Assertion represents an expression in a section of the model.
// For example: r = sub, obj, act
// For example: r = sub, obj, act.
type Assertion struct {
Key string
Value string
@ -147,7 +147,7 @@ func (ast *Assertion) buildConditionalRoleLinks(condRM rbac.ConditionalRoleManag
return nil
}
// addConditionalRoleLinks adds Link to rbac.ConditionalRoleManager and sets the parameters for LinkConditionFunc
// addConditionalRoleLink adds Link to rbac.ConditionalRoleManager and sets the parameters for LinkConditionFunc.
func (ast *Assertion) addConditionalRoleLink(rule []string, domainRule []string) error {
var err error
if len(domainRule) == 0 {

View File

@ -17,8 +17,8 @@ package model
import (
"sync"
"github.com/Knetic/govaluate"
"github.com/casbin/casbin/v2/util"
"github.com/casbin/govaluate"
)
// FunctionMap represents the collection of Function.
@ -53,7 +53,7 @@ func LoadFunctionMap() FunctionMap {
return *fm
}
// GetFunctions return a map with all the functions
// GetFunctions return a map with all the functions.
func (fm *FunctionMap) GetFunctions() map[string]govaluate.ExpressionFunction {
ret := make(map[string]govaluate.ExpressionFunction)

View File

@ -46,7 +46,7 @@ var sectionNameMap = map[string]string{
"m": "matchers",
}
// Minimal required sections for a model to be valid
// Minimal required sections for a model to be valid.
var requiredSections = []string{"r", "p", "e", "m"}
func loadAssertion(model Model, cfg config.ConfigInterface, sec string, key string) bool {
@ -56,7 +56,7 @@ func loadAssertion(model Model, cfg config.ConfigInterface, sec string, key stri
var paramsRegex = regexp.MustCompile(`\((.*?)\)`)
// getParamsToken Get ParamsToken from Assertion.Value
// getParamsToken Get ParamsToken from Assertion.Value.
func getParamsToken(value string) []string {
paramsString := paramsRegex.FindString(value)
if paramsString == "" {

View File

@ -207,15 +207,11 @@ func (model Model) AddPolicy(sec string, ptype string, rule []string) {
i := len(assertion.Policy) - 1
for ; i > 0; i-- {
idx, err := strconv.Atoi(assertion.Policy[i-1][assertion.FieldIndexMap[constant.PriorityIndex]])
if err != nil {
break
}
if idx > idxInsert {
assertion.Policy[i] = assertion.Policy[i-1]
assertion.PolicyMap[strings.Join(assertion.Policy[i-1], DefaultSep)]++
} else {
if err != nil || idx <= idxInsert {
break
}
assertion.Policy[i] = assertion.Policy[i-1]
assertion.PolicyMap[strings.Join(assertion.Policy[i-1], DefaultSep)]++
}
assertion.Policy[i] = rule
assertion.PolicyMap[strings.Join(rule, DefaultSep)] = i

View File

@ -22,8 +22,6 @@ import (
// ContextAdapter provides a context-aware interface for Casbin adapters.
type ContextAdapter interface {
Adapter
// LoadPolicyCtx loads all policy rules from the storage with context.
LoadPolicyCtx(ctx context.Context, model model.Model) error
// SavePolicyCtx saves all policy rules to the storage with context.

View File

@ -14,7 +14,7 @@
package persist
// Dispatcher is the interface for Casbin dispatcher
// Dispatcher is the interface for Casbin dispatcher.
type Dispatcher interface {
// AddPolicies adds policies rule to all instance.
AddPolicies(sec string, ptype string, rules [][]string) error

View File

@ -73,12 +73,12 @@ func (a *AdapterMock) loadPolicyFile(model model.Model, handler func(string, mod
}
}
// SetMockErr sets string to be returned by of the mock during testing
// SetMockErr sets string to be returned by of the mock during testing.
func (a *AdapterMock) SetMockErr(errorToSet string) {
a.errorValue = errorToSet
}
// GetMockErr returns a mock error or nil
// GetMockErr returns a mock error or nil.
func (a *AdapterMock) GetMockErr() error {
var returnError error
if a.errorValue != "" {

View File

@ -59,12 +59,12 @@ func (r *Role) removeRole(role *Role) {
role.removeUser(r)
}
// should only be called inside addRole
// should only be called inside addRole.
func (r *Role) addUser(user *Role) {
r.users.Store(user.name, user)
}
// should only be called inside removeRole
// should only be called inside removeRole.
func (r *Role) removeUser(user *Role) {
r.users.Delete(user.name)
}
@ -195,7 +195,7 @@ func (r *Role) getLinkConditionFuncParams(role *Role, domain string) ([]string,
return params.([]string), ok
}
// RoleManagerImpl provides a default implementation for the RoleManager interface
// RoleManagerImpl provides a default implementation for the RoleManager interface.
type RoleManagerImpl struct {
allRoles *sync.Map
maxHierarchyLevel int
@ -209,20 +209,20 @@ type RoleManagerImpl struct {
// default RoleManager implementation.
func NewRoleManagerImpl(maxHierarchyLevel int) *RoleManagerImpl {
rm := RoleManagerImpl{}
_ = rm.Clear() //init allRoles and matchingFuncCache
_ = rm.Clear() // init allRoles and matchingFuncCache
rm.maxHierarchyLevel = maxHierarchyLevel
rm.SetLogger(&log.DefaultLogger{})
return &rm
}
// use this constructor to avoid rebuild of AddMatchingFunc
// use this constructor to avoid rebuild of AddMatchingFunc.
func newRoleManagerWithMatchingFunc(maxHierarchyLevel int, fn rbac.MatchingFunc) *RoleManagerImpl {
rm := NewRoleManagerImpl(maxHierarchyLevel)
rm.matchingFunc = fn
return rm
}
// rebuilds role cache
// rebuilds role cache.
func (rm *RoleManagerImpl) rebuild() {
roles := rm.allRoles
_ = rm.Clear()
@ -263,7 +263,7 @@ func (rm *RoleManagerImpl) load(name interface{}) (value *Role, ok bool) {
return nil, false
}
// loads or creates a role
// loads or creates a role.
func (rm *RoleManagerImpl) getRole(name string) (r *Role, created bool) {
var role *Role
var ok bool
@ -302,13 +302,13 @@ func (rm *RoleManagerImpl) removeRole(name string) {
}
}
// AddMatchingFunc support use pattern in g
// AddMatchingFunc support use pattern in g.
func (rm *RoleManagerImpl) AddMatchingFunc(name string, fn rbac.MatchingFunc) {
rm.matchingFunc = fn
rm.rebuild()
}
// AddDomainMatchingFunc support use domain pattern in g
// AddDomainMatchingFunc support use domain pattern in g.
func (rm *RoleManagerImpl) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) {
rm.domainMatchingFunc = fn
}
@ -424,13 +424,13 @@ func (rm *RoleManagerImpl) PrintRoles() error {
return nil
}
// GetDomains gets domains that a user has
// GetDomains gets domains that a user has.
func (rm *RoleManagerImpl) GetDomains(name string) ([]string, error) {
domains := []string{defaultDomain}
return domains, nil
}
// GetAllDomains gets all domains
// GetAllDomains gets all domains.
func (rm *RoleManagerImpl) GetAllDomains() ([]string, error) {
domains := []string{defaultDomain}
return domains, nil
@ -458,7 +458,7 @@ func (rm *RoleManagerImpl) Range(fn func(name1, name2 string, domain ...string)
rangeLinks(rm.allRoles, fn)
}
// Deprecated: BuildRelationship is no longer required
// Deprecated: BuildRelationship is no longer required.
func (rm *RoleManagerImpl) BuildRelationship(name1 string, name2 string, domain ...string) error {
return nil
}
@ -486,7 +486,7 @@ func (dm *DomainManager) SetLogger(logger log.Logger) {
dm.logger = logger
}
// AddMatchingFunc support use pattern in g
// AddMatchingFunc support use pattern in g.
func (dm *DomainManager) AddMatchingFunc(name string, fn rbac.MatchingFunc) {
dm.matchingFunc = fn
dm.rmMap.Range(func(key, value interface{}) bool {
@ -495,7 +495,7 @@ func (dm *DomainManager) AddMatchingFunc(name string, fn rbac.MatchingFunc) {
})
}
// AddDomainMatchingFunc support use domain pattern in g
// AddDomainMatchingFunc support use domain pattern in g.
func (dm *DomainManager) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc) {
dm.domainMatchingFunc = fn
dm.rmMap.Range(func(key, value interface{}) bool {
@ -505,7 +505,7 @@ func (dm *DomainManager) AddDomainMatchingFunc(name string, fn rbac.MatchingFunc
dm.rebuild()
}
// clears the map of RoleManagers
// clears the map of RoleManagers.
func (dm *DomainManager) rebuild() {
rmMap := dm.rmMap
_ = dm.Clear()
@ -568,7 +568,7 @@ func (dm *DomainManager) load(name interface{}) (value *RoleManagerImpl, ok bool
return nil, false
}
// load or create a RoleManager instance of domain
// load or create a RoleManager instance of domain.
func (dm *DomainManager) getRoleManager(domain string, store bool) *RoleManagerImpl {
var rm *RoleManagerImpl
var ok bool
@ -599,7 +599,7 @@ func (dm *DomainManager) AddLink(name1 string, name2 string, domains ...string)
if err != nil {
return err
}
roleManager := dm.getRoleManager(domain, true) //create role manager if it does not exist
roleManager := dm.getRoleManager(domain, true) // create role manager if it does not exist
_ = roleManager.AddLink(name1, name2, domains...)
dm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) {
@ -615,7 +615,7 @@ func (dm *DomainManager) DeleteLink(name1 string, name2 string, domains ...strin
if err != nil {
return err
}
roleManager := dm.getRoleManager(domain, true) //create role manager if it does not exist
roleManager := dm.getRoleManager(domain, true) // create role manager if it does not exist
_ = roleManager.DeleteLink(name1, name2, domains...)
dm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) {
@ -679,7 +679,7 @@ func (dm *DomainManager) PrintRoles() error {
return nil
}
// GetDomains gets domains that a user has
// GetDomains gets domains that a user has.
func (dm *DomainManager) GetDomains(name string) ([]string, error) {
var domains []string
dm.rmMap.Range(func(key, value interface{}) bool {
@ -697,18 +697,18 @@ func (dm *DomainManager) GetDomains(name string) ([]string, error) {
return domains, nil
}
// GetAllDomains gets all domains
func (rm *DomainManager) GetAllDomains() ([]string, error) {
// GetAllDomains gets all domains.
func (dm *DomainManager) GetAllDomains() ([]string, error) {
var domains []string
rm.rmMap.Range(func(key, value interface{}) bool {
dm.rmMap.Range(func(key, value interface{}) bool {
domains = append(domains, key.(string))
return true
})
return domains, nil
}
// Deprecated: BuildRelationship is no longer required
func (rm *DomainManager) BuildRelationship(name1 string, name2 string, domain ...string) error {
// Deprecated: BuildRelationship is no longer required.
func (dm *DomainManager) BuildRelationship(name1 string, name2 string, domain ...string) error {
return nil
}
@ -733,7 +733,7 @@ func (crm *ConditionalRoleManager) copyFrom(other *ConditionalRoleManager) {
})
}
// use this constructor to avoid rebuild of AddMatchingFunc
// use this constructor to avoid rebuild of AddMatchingFunc.
func newConditionalRoleManagerWithMatchingFunc(maxHierarchyLevel int, fn rbac.MatchingFunc) *ConditionalRoleManager {
rm := NewConditionalRoleManager(maxHierarchyLevel)
rm.matchingFunc = fn
@ -744,7 +744,7 @@ func newConditionalRoleManagerWithMatchingFunc(maxHierarchyLevel int, fn rbac.Ma
// ConditionalRoleManager implementation.
func NewConditionalRoleManager(maxHierarchyLevel int) *ConditionalRoleManager {
rm := ConditionalRoleManager{}
_ = rm.Clear() //init allRoles and matchingFuncCache
_ = rm.Clear() // init allRoles and matchingFuncCache
rm.maxHierarchyLevel = maxHierarchyLevel
rm.SetLogger(&log.DefaultLogger{})
return &rm
@ -770,7 +770,7 @@ func (crm *ConditionalRoleManager) HasLink(name1 string, name2 string, domains .
}
// hasLinkHelper use the Breadth First Search algorithm to traverse the Role tree
// Judging whether the user has a role (has link) is to judge whether the role node can be reached from the user node
// Judging whether the user has a role (has link) is to judge whether the role node can be reached from the user node.
func (crm *ConditionalRoleManager) hasLinkHelper(targetName string, roles map[string]*Role, level int, domains ...string) bool {
if level < 0 || len(roles) == 0 {
return false
@ -817,12 +817,12 @@ func (crm *ConditionalRoleManager) getNextRoles(currentRole, nextRole *Role, dom
return true
}
// GetLinkConditionFunc get LinkConditionFunc based on userName, roleName
// GetLinkConditionFunc get LinkConditionFunc based on userName, roleName.
func (crm *ConditionalRoleManager) GetLinkConditionFunc(userName, roleName string) (rbac.LinkConditionFunc, bool) {
return crm.GetDomainLinkConditionFunc(userName, roleName, defaultDomain)
}
// GetDomainLinkConditionFunc get LinkConditionFunc based on userName, roleName, domain
// GetDomainLinkConditionFunc get LinkConditionFunc based on userName, roleName, domain.
func (crm *ConditionalRoleManager) GetDomainLinkConditionFunc(userName, roleName, domain string) (rbac.LinkConditionFunc, bool) {
user, userCreated := crm.getRole(userName)
role, roleCreated := crm.getRole(roleName)
@ -840,7 +840,7 @@ func (crm *ConditionalRoleManager) GetDomainLinkConditionFunc(userName, roleName
return user.getLinkConditionFunc(role, domain)
}
// GetLinkConditionFuncParams gets parameters of LinkConditionFunc based on userName, roleName, domain
// GetLinkConditionFuncParams gets parameters of LinkConditionFunc based on userName, roleName, domain.
func (crm *ConditionalRoleManager) GetLinkConditionFuncParams(userName, roleName string, domain ...string) ([]string, bool) {
user, userCreated := crm.getRole(userName)
role, roleCreated := crm.getRole(roleName)
@ -867,12 +867,12 @@ func (crm *ConditionalRoleManager) GetLinkConditionFuncParams(userName, roleName
}
}
// AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc
// AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc.
func (crm *ConditionalRoleManager) AddLinkConditionFunc(userName, roleName string, fn rbac.LinkConditionFunc) {
crm.AddDomainLinkConditionFunc(userName, roleName, defaultDomain, fn)
}
// AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc
// AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc.
func (crm *ConditionalRoleManager) AddDomainLinkConditionFunc(userName, roleName, domain string, fn rbac.LinkConditionFunc) {
user, _ := crm.getRole(userName)
role, _ := crm.getRole(roleName)
@ -880,12 +880,12 @@ func (crm *ConditionalRoleManager) AddDomainLinkConditionFunc(userName, roleName
user.addLinkConditionFunc(role, domain, fn)
}
// SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain
// SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain.
func (crm *ConditionalRoleManager) SetLinkConditionFuncParams(userName, roleName string, params ...string) {
crm.SetDomainLinkConditionFuncParams(userName, roleName, defaultDomain, params...)
}
// SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain
// SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain.
func (crm *ConditionalRoleManager) SetDomainLinkConditionFuncParams(userName, roleName, domain string, params ...string) {
user, _ := crm.getRole(userName)
role, _ := crm.getRole(roleName)
@ -902,20 +902,20 @@ type ConditionalDomainManager struct {
// ConditionalDomainManager implementation.
func NewConditionalDomainManager(maxHierarchyLevel int) *ConditionalDomainManager {
rm := ConditionalDomainManager{}
_ = rm.Clear() //init allRoles and matchingFuncCache
_ = rm.Clear() // init allRoles and matchingFuncCache
rm.maxHierarchyLevel = maxHierarchyLevel
rm.SetLogger(&log.DefaultLogger{})
return &rm
}
func (dm *ConditionalDomainManager) load(name interface{}) (value *ConditionalRoleManager, ok bool) {
if r, ok := dm.rmMap.Load(name); ok {
func (cdm *ConditionalDomainManager) load(name interface{}) (value *ConditionalRoleManager, ok bool) {
if r, ok := cdm.rmMap.Load(name); ok {
return r.(*ConditionalRoleManager), true
}
return nil, false
}
// load or create a ConditionalRoleManager instance of domain
// load or create a ConditionalRoleManager instance of domain.
func (cdm *ConditionalDomainManager) getConditionalRoleManager(domain string, store bool) *ConditionalRoleManager {
var rm *ConditionalRoleManager
var ok bool
@ -956,7 +956,7 @@ func (cdm *ConditionalDomainManager) AddLink(name1 string, name2 string, domains
if err != nil {
return err
}
conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) //create role manager if it does not exist
conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) // create role manager if it does not exist
_ = conditionalRoleManager.AddLink(name1, name2, domain)
cdm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) {
@ -972,7 +972,7 @@ func (cdm *ConditionalDomainManager) DeleteLink(name1 string, name2 string, doma
if err != nil {
return err
}
conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) //create role manager if it does not exist
conditionalRoleManager := cdm.getConditionalRoleManager(domain, true) // create role manager if it does not exist
_ = conditionalRoleManager.DeleteLink(name1, name2, domain)
cdm.rangeAffectedRoleManagers(domain, func(rm *RoleManagerImpl) {
@ -981,7 +981,7 @@ func (cdm *ConditionalDomainManager) DeleteLink(name1 string, name2 string, doma
return nil
}
// AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc
// AddLinkConditionFunc is based on userName, roleName, add LinkConditionFunc.
func (cdm *ConditionalDomainManager) AddLinkConditionFunc(userName, roleName string, fn rbac.LinkConditionFunc) {
cdm.rmMap.Range(func(key, value interface{}) bool {
value.(*ConditionalRoleManager).AddLinkConditionFunc(userName, roleName, fn)
@ -989,7 +989,7 @@ func (cdm *ConditionalDomainManager) AddLinkConditionFunc(userName, roleName str
})
}
// AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc
// AddDomainLinkConditionFunc is based on userName, roleName, domain, add LinkConditionFunc.
func (cdm *ConditionalDomainManager) AddDomainLinkConditionFunc(userName, roleName, domain string, fn rbac.LinkConditionFunc) {
cdm.rmMap.Range(func(key, value interface{}) bool {
value.(*ConditionalRoleManager).AddDomainLinkConditionFunc(userName, roleName, domain, fn)
@ -997,7 +997,7 @@ func (cdm *ConditionalDomainManager) AddDomainLinkConditionFunc(userName, roleNa
})
}
// SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName
// SetLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName.
func (cdm *ConditionalDomainManager) SetLinkConditionFuncParams(userName, roleName string, params ...string) {
cdm.rmMap.Range(func(key, value interface{}) bool {
value.(*ConditionalRoleManager).SetLinkConditionFuncParams(userName, roleName, params...)
@ -1005,7 +1005,7 @@ func (cdm *ConditionalDomainManager) SetLinkConditionFuncParams(userName, roleNa
})
}
// SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain
// SetDomainLinkConditionFuncParams sets parameters of LinkConditionFunc based on userName, roleName, domain.
func (cdm *ConditionalDomainManager) SetDomainLinkConditionFuncParams(userName, roleName, domain string, params ...string) {
cdm.rmMap.Range(func(key, value interface{}) bool {
value.(*ConditionalRoleManager).SetDomainLinkConditionFuncParams(userName, roleName, domain, params...)

View File

@ -58,7 +58,7 @@ type RoleManager interface {
}
// ConditionalRoleManager provides interface to define the operations for managing roles.
// Link with conditions is supported
// Link with conditions is supported.
type ConditionalRoleManager interface {
RoleManager

View File

@ -15,6 +15,7 @@
package casbin
import (
"fmt"
"strings"
"github.com/casbin/casbin/v2/constant"
@ -24,13 +25,21 @@ import (
// GetRolesForUser gets the roles that a user has.
func (e *Enforcer) GetRolesForUser(name string, domain ...string) ([]string, error) {
res, err := e.model["g"]["g"].RM.GetRoles(name, domain...)
rm := e.GetRoleManager()
if rm == nil {
return nil, fmt.Errorf("role manager is not initialized")
}
res, err := rm.GetRoles(name, domain...)
return res, err
}
// GetUsersForRole gets the users that has a role.
func (e *Enforcer) GetUsersForRole(name string, domain ...string) ([]string, error) {
res, err := e.model["g"]["g"].RM.GetUsers(name, domain...)
rm := e.GetRoleManager()
if rm == nil {
return nil, fmt.Errorf("role manager is not initialized")
}
res, err := rm.GetUsers(name, domain...)
return res, err
}
@ -114,7 +123,12 @@ func (e *Enforcer) DeleteUser(user string) (bool, error) {
// Returns false if the role does not exist (aka not affected).
func (e *Enforcer) DeleteRole(role string) (bool, error) {
var err error
res1, err := e.RemoveFilteredGroupingPolicy(1, role)
res1, err := e.RemoveFilteredGroupingPolicy(0, role)
if err != nil {
return res1, err
}
res2, err := e.RemoveFilteredGroupingPolicy(1, role)
if err != nil {
return res1, err
}
@ -123,8 +137,8 @@ func (e *Enforcer) DeleteRole(role string) (bool, error) {
if err != nil {
return false, err
}
res2, err := e.RemoveFilteredPolicy(subIndex, role)
return res1 || res2, err
res3, err := e.RemoveFilteredPolicy(subIndex, role)
return res1 || res2 || res3, err
}
// DeletePermission deletes a permission.
@ -166,12 +180,12 @@ func (e *Enforcer) DeletePermissionsForUser(user string) (bool, error) {
}
// GetPermissionsForUser gets permissions for a user or role.
func (e *Enforcer) GetPermissionsForUser(user string, domain ...string) [][]string {
func (e *Enforcer) GetPermissionsForUser(user string, domain ...string) ([][]string, error) {
return e.GetNamedPermissionsForUser("p", user, domain...)
}
// GetNamedPermissionsForUser gets permissions for a user or role by named policy.
func (e *Enforcer) GetNamedPermissionsForUser(ptype string, user string, domain ...string) [][]string {
func (e *Enforcer) GetNamedPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) {
permission := make([][]string, 0)
for pType, assertion := range e.model["p"] {
if pType != ptype {
@ -187,14 +201,14 @@ func (e *Enforcer) GetNamedPermissionsForUser(ptype string, user string, domain
if len(domain) > 0 {
index, err := e.GetFieldIndex(ptype, constant.DomainIndex)
if err != nil {
return permission
return permission, err
}
args[index] = domain[0]
}
perm := e.GetFilteredNamedPolicy(ptype, 0, args...)
permission = append(permission, perm...)
}
return permission
return permission, nil
}
// HasPermissionForUser determines whether a user has a permission.
@ -214,7 +228,6 @@ func (e *Enforcer) GetImplicitRolesForUser(name string, domain ...string) ([]str
res := []string{}
for _, rm := range e.rmMap {
roleSet := make(map[string]bool)
roleSet[name] = true
q := make([]string, 0)
@ -246,7 +259,6 @@ func (e *Enforcer) GetImplicitUsersForRole(name string, domain ...string) ([]str
res := []string{}
for _, rm := range e.rmMap {
roleSet := make(map[string]bool)
roleSet[name] = true
q := make([]string, 0)
@ -294,10 +306,13 @@ func (e *Enforcer) GetImplicitPermissionsForUser(user string, domain ...string)
// g, alice, admin
//
// GetImplicitPermissionsForUser("alice") can only get: [["admin", "data1", "read"]], whose policy is default policy "p"
// But you can specify the named policy "p2" to get: [["admin", "create"]] by GetNamedImplicitPermissionsForUser("p2","alice")
// But you can specify the named policy "p2" to get: [["admin", "create"]] by GetNamedImplicitPermissionsForUser("p2","alice").
func (e *Enforcer) GetNamedImplicitPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) {
permission := make([][]string, 0)
rm := e.GetRoleManager()
if rm == nil {
return nil, fmt.Errorf("role manager is not initialized")
}
domainIndex, _ := e.GetFieldIndex(ptype, constant.DomainIndex)
for _, rule := range e.model["p"][ptype].Policy {
if len(domain) == 0 {
@ -305,20 +320,21 @@ func (e *Enforcer) GetNamedImplicitPermissionsForUser(ptype string, user string,
if matched {
permission = append(permission, deepCopyPolicy(rule))
}
} else if len(domain) > 1 {
continue
}
if len(domain) > 1 {
return nil, errors.ErrDomainParameter
} else {
d := domain[0]
matched := rm.Match(d, rule[domainIndex])
if !matched {
continue
}
matched, _ = rm.HasLink(user, rule[0], d)
if matched {
newRule := deepCopyPolicy(rule)
newRule[domainIndex] = d
permission = append(permission, newRule)
}
}
d := domain[0]
matched := rm.Match(d, rule[domainIndex])
if !matched {
continue
}
matched, _ = rm.HasLink(user, rule[0], d)
if matched {
newRule := deepCopyPolicy(rule)
newRule[domainIndex] = d
permission = append(permission, newRule)
}
}
return permission, nil
@ -358,7 +374,7 @@ func (e *Enforcer) GetImplicitUsersForPermission(permission ...string) ([]string
return res, nil
}
// GetDomainsForUser gets all domains
// GetDomainsForUser gets all domains.
func (e *Enforcer) GetDomainsForUser(user string) ([]string, error) {
var domains []string
for _, rm := range e.rmMap {
@ -371,7 +387,7 @@ func (e *Enforcer) GetDomainsForUser(user string) ([]string, error) {
return domains, nil
}
// GetImplicitResourcesForUser returns all policies that user obtaining in domain
// GetImplicitResourcesForUser returns all policies that user obtaining in domain.
func (e *Enforcer) GetImplicitResourcesForUser(user string, domain ...string) ([][]string, error) {
permissions, err := e.GetImplicitPermissionsForUser(user, domain...)
if err != nil {
@ -410,7 +426,7 @@ func (e *Enforcer) GetImplicitResourcesForUser(user string, domain ...string) ([
return res, nil
}
// deepCopyPolicy returns a deepcopy version of the policy to prevent changing policies through returned slice
// deepCopyPolicy returns a deepcopy version of the policy to prevent changing policies through returned slice.
func deepCopyPolicy(src []string) []string {
newRule := make([]string, len(src))
copy(newRule, src)
@ -483,6 +499,9 @@ func (e *Enforcer) GetImplicitUsersForResource(resource string) ([][]string, err
subjectIndex, _ := e.GetFieldIndex("p", "sub")
objectIndex, _ := e.GetFieldIndex("p", "obj")
rm := e.GetRoleManager()
if rm == nil {
return nil, fmt.Errorf("role manager is not initialized")
}
isRole := make(map[string]bool)
for _, role := range e.GetAllRoles() {
@ -518,13 +537,16 @@ func (e *Enforcer) GetImplicitUsersForResource(resource string) ([][]string, err
}
// GetImplicitUsersForResourceByDomain return implicit user based on resource and domain.
// Compared to GetImplicitUsersForResource, domain is supported
// Compared to GetImplicitUsersForResource, domain is supported.
func (e *Enforcer) GetImplicitUsersForResourceByDomain(resource string, domain string) ([][]string, error) {
permissions := make([][]string, 0)
subjectIndex, _ := e.GetFieldIndex("p", "sub")
objectIndex, _ := e.GetFieldIndex("p", "obj")
domIndex, _ := e.GetFieldIndex("p", "dom")
rm := e.GetRoleManager()
if rm == nil {
return nil, fmt.Errorf("role manager is not initialized")
}
isRole := make(map[string]bool)

View File

@ -124,14 +124,14 @@ func (e *SyncedEnforcer) DeletePermissionsForUser(user string) (bool, error) {
}
// GetPermissionsForUser gets permissions for a user or role.
func (e *SyncedEnforcer) GetPermissionsForUser(user string, domain ...string) [][]string {
func (e *SyncedEnforcer) GetPermissionsForUser(user string, domain ...string) ([][]string, error) {
e.m.RLock()
defer e.m.RUnlock()
return e.Enforcer.GetPermissionsForUser(user, domain...)
}
// GetNamedPermissionsForUser gets permissions for a user or role by named policy.
func (e *SyncedEnforcer) GetNamedPermissionsForUser(ptype string, user string, domain ...string) [][]string {
func (e *SyncedEnforcer) GetNamedPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) {
e.m.RLock()
defer e.m.RUnlock()
return e.Enforcer.GetNamedPermissionsForUser(ptype, user, domain...)
@ -181,7 +181,7 @@ func (e *SyncedEnforcer) GetImplicitPermissionsForUser(user string, domain ...st
// g, alice, admin
//
// GetImplicitPermissionsForUser("alice") can only get: [["admin", "data1", "read"]], whose policy is default policy "p"
// But you can specify the named policy "p2" to get: [["admin", "create"]] by GetNamedImplicitPermissionsForUser("p2","alice")
// But you can specify the named policy "p2" to get: [["admin", "create"]] by GetNamedImplicitPermissionsForUser("p2","alice").
func (e *SyncedEnforcer) GetNamedImplicitPermissionsForUser(ptype string, user string, domain ...string) ([][]string, error) {
e.m.RLock()
defer e.m.RUnlock()

View File

@ -14,17 +14,27 @@
package casbin
import "github.com/casbin/casbin/v2/constant"
import (
"fmt"
// GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon
"github.com/casbin/casbin/v2/constant"
)
// GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon.
func (e *Enforcer) GetUsersForRoleInDomain(name string, domain string) []string {
res, _ := e.model["g"]["g"].RM.GetUsers(name, domain)
if e.GetRoleManager() == nil {
return nil
}
res, _ := e.GetRoleManager().GetUsers(name, domain)
return res
}
// GetRolesForUserInDomain gets the roles that a user has inside a domain.
func (e *Enforcer) GetRolesForUserInDomain(name string, domain string) []string {
res, _ := e.model["g"]["g"].RM.GetRoles(name, domain)
if e.GetRoleManager() == nil {
return nil
}
res, _ := e.GetRoleManager().GetRoles(name, domain)
return res
}
@ -49,7 +59,10 @@ func (e *Enforcer) DeleteRoleForUserInDomain(user string, role string, domain st
// DeleteRolesForUserInDomain deletes all roles for a user inside a domain.
// Returns false if the user does not have any roles (aka not affected).
func (e *Enforcer) DeleteRolesForUserInDomain(user string, domain string) (bool, error) {
roles, err := e.model["g"]["g"].RM.GetRoles(user, domain)
if e.GetRoleManager() == nil {
return false, fmt.Errorf("role manager is not initialized")
}
roles, err := e.GetRoleManager().GetRoles(user, domain)
if err != nil {
return false, err
}
@ -142,7 +155,10 @@ func (e *Enforcer) DeleteDomains(domains ...string) (bool, error) {
// GetAllDomains would get all domains.
func (e *Enforcer) GetAllDomains() ([]string, error) {
return e.model["g"]["g"].RM.GetAllDomains()
if e.GetRoleManager() == nil {
return nil, fmt.Errorf("role manager is not initialized")
}
return e.GetRoleManager().GetAllDomains()
}
// GetAllRolesByDomain would get all roles associated with the domain.

View File

@ -14,7 +14,7 @@
package casbin
// GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon
// GetUsersForRoleInDomain gets the users that has a role inside a domain. Add by Gordon.
func (e *SyncedEnforcer) GetUsersForRoleInDomain(name string, domain string) []string {
e.m.RLock()
defer e.m.RUnlock()

Some files were not shown because too many files have changed in this diff Show More