Compare commits

...

7 Commits

Author SHA1 Message Date
8757643399 v0.0.359 fix tests
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m18s
2024-01-05 16:55:53 +01:00
42bd4cf58d v0.0.358
Some checks failed
Build Docker and Deploy / Run goext test-suite (push) Failing after 1m22s
2024-01-05 16:53:14 +01:00
413178e2d3 v0.0.357
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m4s
2024-01-05 10:59:06 +01:00
9264a2e99b v0.0.356 exerr.GetMeta
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m9s
2024-01-05 10:43:39 +01:00
2a0471fb3d v0.0.355 sq.Json
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 2m27s
2024-01-05 10:25:05 +01:00
1497c013f9 v0.0.354
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 3m13s
2024-01-05 07:21:43 +01:00
ef78b7467b v0.0.353 add scn.sendmessage
All checks were successful
Build Docker and Deploy / Run goext test-suite (push) Successful in 4m16s
2024-01-04 12:38:03 +01:00
17 changed files with 548 additions and 27 deletions

View File

@@ -41,3 +41,5 @@ Potentially needs `export GOPRIVATE="gogs.mikescher.com"`
| rext | Mike | Regex Wrapper, wraps regexp with a better interface |
| wmo | Mike | Mongo Wrapper, wraps mongodb with a better interface |
| | | |
| scn | Mike | SimpleCloudNotifier |
| | | |

View File

@@ -48,6 +48,9 @@ var (
TypeMongoReflection = NewType("MONGO_REFLECTION", langext.Ptr(500))
TypeMongoInvalidOpt = NewType("MONGO_INVALIDOPT", langext.Ptr(500))
TypeSQLQuery = NewType("SQL_QUERY", langext.Ptr(500))
TypeSQLBuild = NewType("SQL_BUILD", langext.Ptr(500))
TypeWrap = NewType("Wrap", nil)
TypeBindFailURI = NewType("BINDFAIL_URI", langext.Ptr(400))
@@ -65,7 +68,7 @@ var (
TypeUnauthorized = NewType("UNAUTHORIZED", langext.Ptr(401))
TypeAuthFailed = NewType("AUTH_FAILED", langext.Ptr(401))
// other values come the used package
// other values come from the downstream application that uses goext
)
var registeredTypes = dataext.SyncSet[string]{}

View File

@@ -258,6 +258,73 @@ func (ee *ExErr) Depth() int {
}
}
// GetMeta returns the meta value with the specified key
// this method recurses through all wrapped errors and returns the first matching meta value
func (ee *ExErr) GetMeta(key string) (any, bool) {
for curr := ee; curr != nil; curr = curr.OriginalError {
if v, ok := curr.Meta[key]; ok {
return v.Value, true
}
}
return nil, false
}
// GetMetaString functions the same as GetMeta, but returns false if the type does not match
func (ee *ExErr) GetMetaString(key string) (string, bool) {
if v1, ok := ee.GetMeta(key); ok {
if v2, ok := v1.(string); ok {
return v2, true
}
}
return "", false
}
func (ee *ExErr) GetMetaBool(key string) (bool, bool) {
if v1, ok := ee.GetMeta(key); ok {
if v2, ok := v1.(bool); ok {
return v2, true
}
}
return false, false
}
func (ee *ExErr) GetMetaInt(key string) (int, bool) {
if v1, ok := ee.GetMeta(key); ok {
if v2, ok := v1.(int); ok {
return v2, true
}
}
return 0, false
}
func (ee *ExErr) GetMetaFloat32(key string) (float32, bool) {
if v1, ok := ee.GetMeta(key); ok {
if v2, ok := v1.(float32); ok {
return v2, true
}
}
return 0, false
}
func (ee *ExErr) GetMetaFloat64(key string) (float64, bool) {
if v1, ok := ee.GetMeta(key); ok {
if v2, ok := v1.(float64); ok {
return v2, true
}
}
return 0, false
}
func (ee *ExErr) GetMetaTime(key string) (time.Time, bool) {
if v1, ok := ee.GetMeta(key); ok {
if v2, ok := v1.(time.Time); ok {
return v2, true
}
}
return time.Time{}, false
}
// contains test if the supplied error is contained in this error (anywhere in the chain)
func (ee *ExErr) contains(original *ExErr) (*ExErr, bool) {
if original == nil {

View File

@@ -359,6 +359,10 @@ func (j jsonAPIErrResponse) Headers() []string {
return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val })
}
func (j jsonAPIErrResponse) Unwrap() error {
return j.err
}
func Status(sc int) HTTPResponse {
return &emptyHTTPResponse{statusCode: sc}
}

17
go.mod
View File

@@ -5,19 +5,20 @@ go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-sqlite3 v1.14.19 // only needed for tests -.-
github.com/rs/xid v1.5.0
github.com/rs/zerolog v1.31.0
go.mongodb.org/mongo-driver v1.13.1
golang.org/x/crypto v0.17.0
golang.org/x/sys v0.15.0
golang.org/x/term v0.15.0
golang.org/x/sys v0.16.0
golang.org/x/term v0.16.0
github.com/glebarez/go-sqlite v1.22.0 // only needed for tests -.-
)
require (
github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
@@ -25,6 +26,7 @@ require (
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
@@ -35,16 +37,21 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
golang.org/x/arch v0.6.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.37.6 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/sqlite v1.28.0 // indirect
)

38
go.sum
View File

@@ -13,12 +13,16 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/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/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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
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=
@@ -39,6 +43,10 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -60,7 +68,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
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/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
@@ -77,6 +84,8 @@ github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdU
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
@@ -109,8 +118,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -127,8 +136,8 @@ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -140,12 +149,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -158,8 +167,9 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
@@ -167,5 +177,13 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@@ -1,5 +1,5 @@
package goext
const GoextVersion = "0.0.352"
const GoextVersion = "0.0.359"
const GoextVersionTimestamp = "2023-12-29T19:29:36+0100"
const GoextVersionTimestamp = "2024-01-05T16:55:53+0100"

13
scn/scn.go Normal file
View File

@@ -0,0 +1,13 @@
package scn
type Connection struct {
uid string
token string
}
func New(userid string, token string) *Connection {
return &Connection{
uid: userid,
token: token,
}
}

171
scn/send.go Normal file
View File

@@ -0,0 +1,171 @@
package scn
import (
"bytes"
"context"
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
json "gogs.mikescher.com/BlackForestBytes/goext/gojson"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"io"
"net/http"
"time"
)
var (
ErrAuthFailed = exerr.NewType("GOEXT_SCN_AUTHFAILED", nil)
ErrQuota = exerr.NewType("GOEXT_SCN_QUOTAEXCEEDED", nil)
ErrBadRequest = exerr.NewType("GOEXT_SCN_BADREQUEST", nil)
ErrInternalServerErr = exerr.NewType("GOEXT_SCN_INTERNALSERVER", nil)
ErrOther = exerr.NewType("GOEXT_SCN_OTHER", nil)
)
type MessageResponse struct {
ErrHighlight int `json:"errhighlight"`
Error int `json:"error"`
IsPro bool `json:"is_pro"`
Message string `json:"message"`
Messagecount int `json:"messagecount"`
Quota int `json:"quota"`
QuotaMax int `json:"quota_max"`
SCNMessageID string `json:"scn_msg_id"`
Success bool `json:"success"`
SuppressSend bool `json:"suppress_send"`
}
type MessageErrResponse struct {
Errhighlight int `json:"errhighlight"`
Error int `json:"error"`
Message string `json:"message"`
Success bool `json:"success"`
}
type MessageBuilder struct {
conn *Connection
title string
content *string
channel *string
time *time.Time
sendername *string
priority *int
}
func (c *Connection) Message(title string) *MessageBuilder {
return &MessageBuilder{conn: c, title: title}
}
func (c *MessageBuilder) Channel(channel string) *MessageBuilder {
c.channel = &channel
return c
}
func (c *MessageBuilder) Content(content string) *MessageBuilder {
c.content = &content
return c
}
func (c *MessageBuilder) Time(t time.Time) *MessageBuilder {
c.time = &t
return c
}
func (c *MessageBuilder) SenderName(sn string) *MessageBuilder {
c.sendername = &sn
return c
}
func (c *MessageBuilder) Priority(p int) *MessageBuilder {
c.priority = &p
return c
}
func (c *MessageBuilder) Send(ctx context.Context) (MessageResponse, error) {
client := http.Client{Timeout: 5 * time.Second}
body := langext.H{}
body["user_id"] = c.conn.uid
body["key"] = c.conn.token
if c.channel != nil {
body["channel"] = *c.channel
}
body["title"] = c.title
if c.content != nil {
body["content"] = *c.content
}
if c.sendername != nil {
body["content"] = *c.sendername
}
if c.time != nil {
body["timestamp"] = c.time.Unix()
}
if c.priority != nil {
body["priority"] = *c.priority
}
body["msg_id"] = langext.MustHexUUID()
rawbody, err := json.Marshal(body)
if err != nil {
return MessageResponse{}, err
}
req, err := http.NewRequestWithContext(ctx, "POST", "https://simplecloudnotifier.de/", bytes.NewBuffer(rawbody))
if err != nil {
return MessageResponse{}, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return MessageResponse{}, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
raw, err := io.ReadAll(resp.Body)
if err != nil {
return MessageResponse{}, err
}
var mr MessageResponse
err = json.Unmarshal(raw, &mr)
if err != nil {
return MessageResponse{}, err
}
return mr, nil
} else {
errMessage := resp.Status
if raw, err := io.ReadAll(resp.Body); err == nil {
var mr MessageErrResponse
if err = json.Unmarshal(raw, &mr); err == nil {
errMessage = mr.Message
}
}
if resp.StatusCode == 400 {
return MessageResponse{}, exerr.New(ErrBadRequest, errMessage).Build()
}
if resp.StatusCode == 401 {
return MessageResponse{}, exerr.New(ErrAuthFailed, errMessage).Build()
}
if resp.StatusCode == 403 {
return MessageResponse{}, exerr.New(ErrQuota, errMessage).Build()
}
if resp.StatusCode == 500 {
return MessageResponse{}, exerr.New(ErrInternalServerErr, errMessage).Build()
}
return MessageResponse{}, exerr.New(ErrOther, errMessage).Build()
}
}

24
scn/send_test.go Normal file
View File

@@ -0,0 +1,24 @@
package scn
import (
"context"
"fmt"
"testing"
)
func TestSendSCN(t *testing.T) {
t.Skip()
return
mr, err := New("TODO", "TODO").
Message("Hello Test").
Content("This is a test / goext").
Send(context.Background())
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", mr)
}

71
sq/builder.go Normal file
View File

@@ -0,0 +1,71 @@
package sq
import (
"fmt"
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
"reflect"
"strings"
)
func BuildUpdateStatement(q Queryable, tableName string, obj any, idColumn string) (string, PP, error) {
rval := reflect.ValueOf(obj)
rtyp := rval.Type()
params := PP{}
setClauses := make([]string, 0)
matchClause := ""
for i := 0; i < rtyp.NumField(); i++ {
rsfield := rtyp.Field(i)
rvfield := rval.Field(i)
if !rsfield.IsExported() {
continue
}
columnName := rsfield.Tag.Get("db")
if columnName == "" || columnName == "-" {
continue
}
if idColumn == columnName {
idValue, err := convertValueToDB(q, rvfield.Interface())
if err != nil {
return "", nil, err
}
matchClause = fmt.Sprintf("(%s = :%s)", columnName, params.Add(idValue))
continue
}
if rsfield.Type.Kind() == reflect.Ptr && rvfield.IsNil() {
setClauses = append(setClauses, fmt.Sprintf("%s = NULL", columnName))
} else {
val, err := convertValueToDB(q, rvfield.Interface())
if err != nil {
return "", nil, err
}
setClauses = append(setClauses, fmt.Sprintf("(%s = :%s)", columnName, params.Add(val)))
}
}
if len(setClauses) == 0 {
return "", nil, exerr.New(exerr.TypeSQLBuild, "no updates clauses found in object").Build()
}
if matchClause == "" {
return "", nil, exerr.New(exerr.TypeSQLBuild, "id column not found in object").Build()
}
//goland:noinspection SqlNoDataSourceInspection
return fmt.Sprintf("UPDATE %s SET %s WHERE %s", tableName, strings.Join(setClauses, ", "), matchClause), params, nil
}

87
sq/builder_test.go Normal file
View File

@@ -0,0 +1,87 @@
package sq
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/glebarez/go-sqlite"
"github.com/jmoiron/sqlx"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/tst"
"path/filepath"
"testing"
)
type dummyQueryable struct {
}
func (d dummyQueryable) Exec(ctx context.Context, sql string, prep PP) (sql.Result, error) {
return nil, errors.New("err")
}
func (d dummyQueryable) Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error) {
return nil, errors.New("err")
}
func (d dummyQueryable) ListConverter() []DBTypeConverter {
return nil
}
func TestCreateUpdateStatement(t *testing.T) {
type request struct {
ID string `json:"id" db:"id"`
Timestamp int `json:"timestamp" db:"timestamp"`
StrVal string `json:"strVal" db:"str_val"`
FloatVal float64 `json:"floatVal" db:"float_val"`
Dummy bool `json:"dummyBool" db:"dummy_bool"`
JsonVal JsonObj `json:"jsonVal" db:"json_val"`
}
if !langext.InArray("sqlite3", sql.Drivers()) {
sqlite.RegisterAsSQLITE3()
}
ctx := context.Background()
dbdir := t.TempDir()
dbfile1 := filepath.Join(dbdir, langext.MustHexUUID()+".sqlite3")
url := fmt.Sprintf("file:%s?_pragma=journal_mode(%s)&_pragma=timeout(%d)&_pragma=foreign_keys(%s)&_pragma=busy_timeout(%d)", dbfile1, "DELETE", 1000, "true", 1000)
xdb := tst.Must(sqlx.Open("sqlite", url))(t)
db := NewDB(xdb)
db.RegisterDefaultConverter()
_, err := db.Exec(ctx, "CREATE TABLE `requests` ( id TEXT NOT NULL, timestamp INTEGER NOT NULL, PRIMARY KEY (id) ) STRICT", PP{})
tst.AssertNoErr(t, err)
sqlStr, pp, err := BuildUpdateStatement(db, "requests", request{
ID: "9927",
Timestamp: 12321,
StrVal: "hello world",
Dummy: true,
FloatVal: 3.14159,
JsonVal: JsonObj{
"firs": 1,
"second": true,
},
}, "id")
tst.AssertNoErr(t, err)
fmt.Printf("\n\n")
fmt.Printf("######## PP:\n")
for k, v := range pp {
fmt.Printf("['%s'] => %+v\n", k, v)
}
fmt.Printf("\n\n")
fmt.Printf("######## SQL:\n%+v\n\n", sqlStr)
fmt.Printf("\n\n")
}

View File

@@ -1,6 +1,7 @@
package sq
import (
"encoding/json"
"errors"
"fmt"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
@@ -76,6 +77,34 @@ var ConverterRFC339NanoTimeToString = NewDBTypeConverter[rfctime.RFC3339NanoTime
return rfctime.NewRFC3339Nano(t), nil
})
var ConverterJsonObjToString = NewDBTypeConverter[JsonObj, string](func(v JsonObj) (string, error) {
mrsh, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(mrsh), nil
}, func(v string) (JsonObj, error) {
var mrsh JsonObj
if err := json.Unmarshal([]byte(v), &mrsh); err != nil {
return JsonObj{}, err
}
return mrsh, nil
})
var ConverterJsonArrToString = NewDBTypeConverter[JsonArr, string](func(v JsonArr) (string, error) {
mrsh, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(mrsh), nil
}, func(v string) (JsonArr, error) {
var mrsh JsonArr
if err := json.Unmarshal([]byte(v), &mrsh); err != nil {
return JsonArr{}, err
}
return mrsh, nil
})
type dbTypeConverterImpl[TModelData any, TDBData any] struct {
dbTypeString string
modelTypeString string

View File

@@ -148,4 +148,6 @@ func (db *database) RegisterDefaultConverter() {
db.RegisterConverter(ConverterRFCUnixTimeToUnixSeconds)
db.RegisterConverter(ConverterRFC339TimeToString)
db.RegisterConverter(ConverterRFC339NanoTimeToString)
db.RegisterConverter(ConverterJsonObjToString)
db.RegisterConverter(ConverterJsonArrToString)
}

5
sq/json.go Normal file
View File

@@ -0,0 +1,5 @@
package sq
type JsonObj map[string]any
type JsonArr []any

View File

@@ -1,5 +1,7 @@
package sq
import "gogs.mikescher.com/BlackForestBytes/goext/langext"
type PP map[string]any
func Join(pps ...PP) PP {
@@ -11,3 +13,13 @@ func Join(pps ...PP) PP {
}
return r
}
func (pp *PP) Add(v any) string {
id := PPID()
(*pp)[id] = v
return id
}
func PPID() string {
return "p_" + langext.RandBase62(8)
}

View File

@@ -2,9 +2,10 @@ package sq
import (
"context"
"database/sql"
"fmt"
"github.com/glebarez/go-sqlite"
"github.com/jmoiron/sqlx"
"github.com/mattn/go-sqlite3"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/rfctime"
"gogs.mikescher.com/BlackForestBytes/goext/tst"
@@ -20,18 +21,20 @@ func TestTypeConverter1(t *testing.T) {
Timestamp time.Time `db:"timestamp"`
}
if !langext.InArray("sqlite3", sql.Drivers()) {
sqlite.RegisterAsSQLITE3()
}
ctx := context.Background()
dbdir := t.TempDir()
dbfile1 := filepath.Join(dbdir, langext.MustHexUUID()+".sqlite3")
sqlite3.Version() // ensure loaded
tst.AssertNoErr(t, os.MkdirAll(dbdir, os.ModePerm))
url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", dbfile1, "DELETE", 1000, "true", 1000)
url := fmt.Sprintf("file:%s?_pragma=journal_mode(%s)&_pragma=timeout(%d)&_pragma=foreign_keys(%s)&_pragma=busy_timeout(%d)", dbfile1, "DELETE", 1000, "true", 1000)
xdb := tst.Must(sqlx.Open("sqlite3", url))(t)
xdb := tst.Must(sqlx.Open("sqlite", url))(t)
db := NewDB(xdb)
db.RegisterDefaultConverter()
@@ -47,6 +50,11 @@ func TestTypeConverter1(t *testing.T) {
}
func TestTypeConverter2(t *testing.T) {
if !langext.InArray("sqlite3", sql.Drivers()) {
sqlite.RegisterAsSQLITE3()
}
type RequestData struct {
ID string `db:"id"`
Timestamp rfctime.UnixMilliTime `db:"timestamp"`
@@ -57,13 +65,11 @@ func TestTypeConverter2(t *testing.T) {
dbdir := t.TempDir()
dbfile1 := filepath.Join(dbdir, langext.MustHexUUID()+".sqlite3")
sqlite3.Version() // ensure loaded
tst.AssertNoErr(t, os.MkdirAll(dbdir, os.ModePerm))
url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", dbfile1, "DELETE", 1000, "true", 1000)
url := fmt.Sprintf("file:%s?_pragma=journal_mode(%s)&_pragma=timeout(%d)&_pragma=foreign_keys(%s)&_pragma=busy_timeout(%d)", dbfile1, "DELETE", 1000, "true", 1000)
xdb := tst.Must(sqlx.Open("sqlite3", url))(t)
xdb := tst.Must(sqlx.Open("sqlite", url))(t)
db := NewDB(xdb)
db.RegisterDefaultConverter()