Compare commits

...

11 Commits

Author SHA1 Message Date
74d42637e7 v0.0.222 forgot status code 2023-08-06 19:11:59 +02:00
0c05bcf29b v0.0.221 download file data 2023-08-06 19:10:31 +02:00
9136143f2f v0.0.220 add ginext.bufferBody 2023-08-03 09:09:27 +02:00
2f1b784dc2 v0.0.219 implement error.Is(*) for exerr 2023-07-28 15:42:12 +02:00
190584e0e6 v0.0.218 bf 2023-07-27 17:16:30 +02:00
b7003b9ec9 v0.0.217 2023-07-27 17:12:41 +02:00
4f871271e8 v0.0.216 2023-07-27 17:00:53 +02:00
91f4793678 v0.0.215 Add (ee *ExErr) ToAPIJson 2023-07-27 14:37:11 +02:00
3b30bb049e v0.0.214 reassign innerctx 2023-07-27 09:58:10 +02:00
f0c5b36ea9 v0.0.213 inject gin key value pairs into context 2023-07-27 09:46:06 +02:00
647ec64c3b v0.0.212 2023-07-26 10:44:26 +02:00
16 changed files with 194 additions and 47 deletions

View File

@@ -31,13 +31,13 @@ type EnumDef struct {
Values []EnumDefVal Values []EnumDefVal
} }
var rexPackage = rext.W(regexp.MustCompile("^package\\s+(?P<name>[A-Za-z0-9_]+)\\s*$")) var rexPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]+)\s*$`))
var rexEnumDef = rext.W(regexp.MustCompile("^\\s*type\\s+(?P<name>[A-Za-z0-9_]+)\\s+(?P<type>[A-Za-z0-9_]+)\\s*//\\s*(@enum:type).*$")) var rexEnumDef = rext.W(regexp.MustCompile(`^\s*type\s+(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*//\s*(@enum:type).*$`))
var rexValueDef = rext.W(regexp.MustCompile("^\\s*(?P<name>[A-Za-z0-9_]+)\\s+(?P<type>[A-Za-z0-9_]+)\\s*=\\s*(?P<value>(\"[A-Za-z0-9_:]+\"|[0-9]+))\\s*(//(?P<descr>.*))?.*$")) var rexValueDef = rext.W(regexp.MustCompile(`^\s*(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*=\s*(?P<value>("[A-Za-z0-9_:]+"|[0-9]+))\s*(//(?P<descr>.*))?.*$`))
var rexChecksumConst = rext.W(regexp.MustCompile("const ChecksumGenerator = \"(?P<cs>[A-Za-z0-9_]*)\"")) var rexChecksumConst = rext.W(regexp.MustCompile(`const ChecksumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
func GenerateEnumSpecs(sourceDir string, destFile string) error { func GenerateEnumSpecs(sourceDir string, destFile string) error {

View File

@@ -22,7 +22,7 @@ type ExErr struct {
WrappedErrType string `json:"wrappedErrType"` WrappedErrType string `json:"wrappedErrType"`
Caller string `json:"caller"` Caller string `json:"caller"`
OriginalError *ExErr OriginalError *ExErr `json:"originalError"`
Meta MetaMap `json:"meta"` Meta MetaMap `json:"meta"`
} }
@@ -31,10 +31,16 @@ func (ee *ExErr) Error() string {
return ee.Message return ee.Message
} }
// Unwrap must be implemented so that some error.XXX methods work
func (ee *ExErr) Unwrap() error { func (ee *ExErr) Unwrap() error {
return ee.OriginalError return ee.OriginalError
} }
// Is must be implemented so that error.Is(x) works
func (ee *ExErr) Is(e error) bool {
return IsFrom(ee, e)
}
func (ee *ExErr) Log(evt *zerolog.Event) { func (ee *ExErr) Log(evt *zerolog.Event) {
evt.Msg(ee.FormatLog(LogPrintFull)) evt.Msg(ee.FormatLog(LogPrintFull))
} }

View File

@@ -3,12 +3,13 @@ package exerr
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
json "gogs.mikescher.com/BlackForestBytes/goext/gojson" json "gogs.mikescher.com/BlackForestBytes/goext/gojson"
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"net/http" "net/http"
"time" "time"
) )
func (ee *ExErr) toJson(depth int) gin.H { func (ee *ExErr) toJson(depth int, applyExtendListener bool) langext.H {
ginJson := gin.H{} ginJson := langext.H{}
if ee.UniqueID != "" { if ee.UniqueID != "" {
ginJson["id"] = ee.UniqueID ginJson["id"] = ee.UniqueID
@@ -38,21 +39,51 @@ func (ee *ExErr) toJson(depth int) gin.H {
ginJson["wrappedErrType"] = ee.WrappedErrType ginJson["wrappedErrType"] = ee.WrappedErrType
} }
if ee.OriginalError != nil { if ee.OriginalError != nil {
ginJson["original"] = ee.OriginalError.toJson(depth + 1) ginJson["original"] = ee.OriginalError.toJson(depth+1, applyExtendListener)
} }
pkgconfig.ExtendGinDataOutput(ee, depth, ginJson) if applyExtendListener {
pkgconfig.ExtendGinDataOutput(ee, depth, ginJson)
}
return ginJson return ginJson
} }
// ToAPIJson converts the ExError to a json object
// (the same object as used in the Output(gin) method)
//
// Parameters:
// - [applyExtendListener]: if false the pkgconfig.ExtendGinOutput / pkgconfig.ExtendGinDataOutput will not be applied
// - [includeWrappedErrors]: if false we do not include the recursive/wrapped errors in `__data`
func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool) langext.H {
apiOutput := langext.H{
"errorid": ee.UniqueID,
"message": ee.RecursiveMessage(),
"errorcode": ee.RecursiveType().Key,
"category": ee.RecursiveCategory().Category,
}
if includeWrappedErrors {
apiOutput["__data"] = ee.toJson(0, applyExtendListener)
}
if applyExtendListener {
pkgconfig.ExtendGinOutput(ee, apiOutput)
}
return apiOutput
}
func (ee *ExErr) Output(g *gin.Context) { func (ee *ExErr) Output(g *gin.Context) {
warnOnPkgConfigNotInitialized()
var statuscode = http.StatusInternalServerError var statuscode = http.StatusInternalServerError
var baseCat = ee.RecursiveCategory() var baseCat = ee.RecursiveCategory()
var baseType = ee.RecursiveType() var baseType = ee.RecursiveType()
var baseStatuscode = ee.RecursiveStatuscode() var baseStatuscode = ee.RecursiveStatuscode()
var baseMessage = ee.RecursiveMessage()
if baseCat == CatUser { if baseCat == CatUser {
statuscode = http.StatusBadRequest statuscode = http.StatusBadRequest
@@ -66,20 +97,7 @@ func (ee *ExErr) Output(g *gin.Context) {
statuscode = *baseType.DefaultStatusCode statuscode = *baseType.DefaultStatusCode
} }
warnOnPkgConfigNotInitialized() ginOutput := ee.ToAPIJson(true, pkgconfig.ExtendedGinOutput)
ginOutput := gin.H{
"errorid": ee.UniqueID,
"message": baseMessage,
"errorcode": baseType.Key,
"category": baseCat.Category,
}
if pkgconfig.ExtendedGinOutput {
ginOutput["__data"] = ee.toJson(0)
}
pkgconfig.ExtendGinOutput(ee, ginOutput)
g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true}) g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true})
} }

View File

@@ -24,6 +24,8 @@ func IsFrom(e error, original error) bool {
if e == nil { if e == nil {
return false return false
} }
//goland:noinspection GoDirectComparisonOfErrors
if e == original { if e == original {
return true return true
} }

View File

@@ -14,6 +14,9 @@ type AppContext struct {
} }
func CreateAppContext(g *gin.Context, innerCtx context.Context, cancelFn context.CancelFunc) *AppContext { func CreateAppContext(g *gin.Context, innerCtx context.Context, cancelFn context.CancelFunc) *AppContext {
for key, value := range g.Keys {
innerCtx = context.WithValue(innerCtx, key, value)
}
return &AppContext{ return &AppContext{
inner: innerCtx, inner: innerCtx,
cancelFunc: cancelFn, cancelFunc: cancelFn,
@@ -38,6 +41,10 @@ func (ac *AppContext) Value(key any) any {
return ac.inner.Value(key) return ac.inner.Value(key)
} }
func (ac *AppContext) Set(key, value any) {
ac.inner = context.WithValue(ac.inner, key, value)
}
func (ac *AppContext) Cancel() { func (ac *AppContext) Cancel() {
ac.cancelled = true ac.cancelled = true
ac.cancelFunc() ac.cancelFunc()

View File

@@ -0,0 +1,14 @@
package ginext
import (
"github.com/gin-gonic/gin"
"gogs.mikescher.com/BlackForestBytes/goext/dataext"
)
func BodyBuffer() gin.HandlerFunc {
return func(g *gin.Context) {
if g.Request.Body != nil {
g.Request.Body = dataext.NewBufferedReadCloser(g.Request.Body)
}
}
}

View File

@@ -18,6 +18,7 @@ type GinWrapper struct {
allowCors bool allowCors bool
ginDebug bool ginDebug bool
bufferBody bool
requestTimeout time.Duration requestTimeout time.Duration
routeSpecs []ginRouteSpec routeSpecs []ginRouteSpec
@@ -30,7 +31,13 @@ type ginRouteSpec struct {
Handler string Handler string
} }
func NewEngine(allowCors bool, ginDebug bool, timeout time.Duration) *GinWrapper { // NewEngine creates a new (wrapped) ginEngine
// Parameters are:
// - [allowCors] Add cors handler to allow all CORS requests on the default http methods
// - [ginDebug] Set gin.debug to true (adds more logs)
// - [bufferBody] Buffers the input body stream, this way the ginext error handler can later include the whole request body
// - [timeout] The default handler timeout
func NewEngine(allowCors bool, ginDebug bool, bufferBody bool, timeout time.Duration) *GinWrapper {
engine := gin.New() engine := gin.New()
wrapper := &GinWrapper{ wrapper := &GinWrapper{
@@ -38,6 +45,7 @@ func NewEngine(allowCors bool, ginDebug bool, timeout time.Duration) *GinWrapper
SuppressGinLogs: false, SuppressGinLogs: false,
allowCors: allowCors, allowCors: allowCors,
ginDebug: ginDebug, ginDebug: ginDebug,
bufferBody: bufferBody,
requestTimeout: timeout, requestTimeout: timeout,
} }

View File

@@ -113,6 +113,31 @@ func (j fileHTTPResponse) WithHeader(k string, v string) HTTPResponse {
return j return j
} }
type downloadDataHTTPResponse struct {
statusCode int
mimetype string
data []byte
filename *string
headers []headerval
}
func (j downloadDataHTTPResponse) Write(g *gin.Context) {
g.Header("Content-Type", j.mimetype) // if we don't set it here gin does weird file-sniffing later...
if j.filename != nil {
g.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", *j.filename))
}
for _, v := range j.headers {
g.Header(v.Key, v.Val)
}
g.Data(j.statusCode, j.mimetype, j.data)
}
func (j downloadDataHTTPResponse) WithHeader(k string, v string) HTTPResponse {
j.headers = append(j.headers, headerval{k, v})
return j
}
type redirectHTTPResponse struct { type redirectHTTPResponse struct {
statusCode int statusCode int
url string url string
@@ -166,6 +191,10 @@ func Download(mimetype string, filepath string, filename string) HTTPResponse {
return &fileHTTPResponse{mimetype: mimetype, filepath: filepath, filename: &filename} return &fileHTTPResponse{mimetype: mimetype, filepath: filepath, filename: &filename}
} }
func DownloadData(status int, mimetype string, filename string, data []byte) HTTPResponse {
return &downloadDataHTTPResponse{statusCode: status, mimetype: mimetype, data: data, filename: &filename}
}
func Redirect(sc int, newURL string) HTTPResponse { func Redirect(sc int, newURL string) HTTPResponse {
return &redirectHTTPResponse{statusCode: sc, url: newURL} return &redirectHTTPResponse{statusCode: sc, url: newURL}
} }

View File

@@ -111,6 +111,13 @@ func (w *GinRouteBuilder) Use(middleware ...gin.HandlerFunc) *GinRouteBuilder {
func (w *GinRouteBuilder) Handle(handler WHandlerFunc) { func (w *GinRouteBuilder) Handle(handler WHandlerFunc) {
if w.routes.wrapper.bufferBody {
arr := make([]gin.HandlerFunc, len(w.handlers)+1)
arr = append(arr, BodyBuffer())
arr = append(arr, w.handlers...)
w.handlers = arr
}
middlewareNames := langext.ArrMap(w.handlers, func(v gin.HandlerFunc) string { return nameOfFunction(v) }) middlewareNames := langext.ArrMap(w.handlers, func(v gin.HandlerFunc) string { return nameOfFunction(v) })
handlerName := nameOfFunction(handler) handlerName := nameOfFunction(handler)
@@ -162,7 +169,7 @@ func nameOfFunction(f any) string {
fname = fname[:len(fname)-len("-fm")] fname = fname[:len(fname)-len("-fm")]
} }
suffix := rext.W(regexp.MustCompile("\\.func[0-9]+$")) suffix := rext.W(regexp.MustCompile(`\.func[0-9]+(?:\.[0-9]+)*$`))
if match, ok := suffix.MatchFirst(fname); ok { if match, ok := suffix.MatchFirst(fname); ok {
fname = fname[:len(fname)-match.FullMatch().Length()] fname = fname[:len(fname)-match.FullMatch().Length()]

14
go.mod
View File

@@ -6,11 +6,11 @@ require (
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/jmoiron/sqlx v1.3.5 github.com/jmoiron/sqlx v1.3.5
github.com/rs/xid v1.5.0 github.com/rs/xid v1.5.0
github.com/rs/zerolog v1.29.1 github.com/rs/zerolog v1.30.0
go.mongodb.org/mongo-driver v1.12.0 go.mongodb.org/mongo-driver v1.12.1
golang.org/x/crypto v0.11.0 golang.org/x/crypto v0.12.0
golang.org/x/sys v0.10.0 golang.org/x/sys v0.11.0
golang.org/x/term v0.10.0 golang.org/x/term v0.11.0
) )
require ( require (
@@ -42,9 +42,9 @@ require (
github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
golang.org/x/arch v0.4.0 // indirect golang.org/x/arch v0.4.0 // indirect
golang.org/x/net v0.12.0 // indirect golang.org/x/net v0.14.0 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sync v0.3.0 // indirect
golang.org/x/text v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

16
go.sum
View File

@@ -87,6 +87,8 @@ github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 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.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -122,6 +124,8 @@ go.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNH
go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE=
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE=
go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@@ -133,6 +137,8 @@ golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -141,6 +147,10 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -166,6 +176,8 @@ 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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
@@ -174,6 +186,8 @@ golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -184,6 +198,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@@ -1,5 +1,5 @@
package goext package goext
const GoextVersion = "0.0.211" const GoextVersion = "0.0.222"
const GoextVersionTimestamp = "2023-07-26T10:40:42+0200" const GoextVersionTimestamp = "2023-08-06T19:11:59+0200"

View File

@@ -8,7 +8,7 @@ import (
func TestGroupByNameOrEmpty1(t *testing.T) { func TestGroupByNameOrEmpty1(t *testing.T) {
regex1 := W(regexp.MustCompile("0(?P<group1>A+)B(?P<group2>C+)0")) regex1 := W(regexp.MustCompile(`0(?P<group1>A+)B(?P<group2>C+)0`))
match1, ok1 := regex1.MatchFirst("Hello 0AAAABCCC0 Bye.") match1, ok1 := regex1.MatchFirst("Hello 0AAAABCCC0 Bye.")
@@ -26,7 +26,7 @@ func TestGroupByNameOrEmpty1(t *testing.T) {
func TestGroupByNameOrEmpty2(t *testing.T) { func TestGroupByNameOrEmpty2(t *testing.T) {
regex1 := W(regexp.MustCompile("0(?P<group1>A+)B(?P<group2>C+)(?P<group3>C+)?0")) regex1 := W(regexp.MustCompile(`0(?P<group1>A+)B(?P<group2>C+)(?P<group3>C+)?0`))
match1, ok1 := regex1.MatchFirst("Hello 0AAAABCCC0 Bye.") match1, ok1 := regex1.MatchFirst("Hello 0AAAABCCC0 Bye.")

View File

@@ -39,7 +39,7 @@ func HashSqliteSchema(ctx context.Context, schemaStr string) (string, error) {
return HashSqliteDatabase(ctx, db) return HashSqliteDatabase(ctx, db)
} }
func HashSqliteDatabase(ctx context.Context, db DB) (string, error) { func HashSqliteDatabase(ctx context.Context, db Queryable) (string, error) {
ss, err := CreateSqliteDatabaseSchemaString(ctx, db) ss, err := CreateSqliteDatabaseSchemaString(ctx, db)
if err != nil { if err != nil {
return "", err return "", err
@@ -50,7 +50,7 @@ func HashSqliteDatabase(ctx context.Context, db DB) (string, error) {
return hex.EncodeToString(cs[:]), nil return hex.EncodeToString(cs[:]), nil
} }
func CreateSqliteDatabaseSchemaString(ctx context.Context, db DB) (string, error) { func CreateSqliteDatabaseSchemaString(ctx context.Context, db Queryable) (string, error) {
type colInfo struct { type colInfo struct {
Name string `db:"name"` Name string `db:"name"`

View File

@@ -7,24 +7,40 @@ import (
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
) )
type TxStatus string
const (
TxStatusInitial TxStatus = "INITIAL"
TxStatusActive TxStatus = "ACTIVE"
TxStatusComitted TxStatus = "COMMITTED"
TxStatusRollback TxStatus = "ROLLBACK"
)
type Tx interface { type Tx interface {
Rollback() error Rollback() error
Commit() error Commit() error
Status() TxStatus
Exec(ctx context.Context, sql string, prep PP) (sql.Result, error) Exec(ctx context.Context, sql string, prep PP) (sql.Result, error)
Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error) Query(ctx context.Context, sql string, prep PP) (*sqlx.Rows, error)
} }
type transaction struct { type transaction struct {
tx *sqlx.Tx tx *sqlx.Tx
id uint16 id uint16
lstr []Listener lstr []Listener
status TxStatus
execCtr int
queryCtr int
} }
func NewTransaction(xtx *sqlx.Tx, txid uint16, lstr []Listener) Tx { func NewTransaction(xtx *sqlx.Tx, txid uint16, lstr []Listener) Tx {
return &transaction{ return &transaction{
tx: xtx, tx: xtx,
id: txid, id: txid,
lstr: lstr, lstr: lstr,
status: TxStatusInitial,
execCtr: 0,
queryCtr: 0,
} }
} }
@@ -38,6 +54,10 @@ func (tx *transaction) Rollback() error {
result := tx.tx.Rollback() result := tx.tx.Rollback()
if result == nil {
tx.status = TxStatusRollback
}
for _, v := range tx.lstr { for _, v := range tx.lstr {
v.PostTxRollback(tx.id, result) v.PostTxRollback(tx.id, result)
} }
@@ -55,6 +75,10 @@ func (tx *transaction) Commit() error {
result := tx.tx.Commit() result := tx.tx.Commit()
if result == nil {
tx.status = TxStatusComitted
}
for _, v := range tx.lstr { for _, v := range tx.lstr {
v.PostTxRollback(tx.id, result) v.PostTxRollback(tx.id, result)
} }
@@ -73,6 +97,10 @@ func (tx *transaction) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Re
res, err := tx.tx.NamedExecContext(ctx, sqlstr, prep) res, err := tx.tx.NamedExecContext(ctx, sqlstr, prep)
if tx.status == TxStatusInitial && err == nil {
tx.status = TxStatusActive
}
for _, v := range tx.lstr { for _, v := range tx.lstr {
v.PostExec(langext.Ptr(tx.id), origsql, sqlstr, prep) v.PostExec(langext.Ptr(tx.id), origsql, sqlstr, prep)
} }
@@ -94,6 +122,10 @@ func (tx *transaction) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx
rows, err := sqlx.NamedQueryContext(ctx, tx.tx, sqlstr, prep) rows, err := sqlx.NamedQueryContext(ctx, tx.tx, sqlstr, prep)
if tx.status == TxStatusInitial && err == nil {
tx.status = TxStatusActive
}
for _, v := range tx.lstr { for _, v := range tx.lstr {
v.PostQuery(langext.Ptr(tx.id), origsql, sqlstr, prep) v.PostQuery(langext.Ptr(tx.id), origsql, sqlstr, prep)
} }
@@ -103,3 +135,11 @@ func (tx *transaction) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx
} }
return rows, nil return rows, nil
} }
func (tx *transaction) Status() TxStatus {
return tx.status
}
func (tx *transaction) Traffic() (int, int) {
return tx.execCtr, tx.queryCtr
}

View File

@@ -71,12 +71,12 @@ func SupportsColors() bool {
} }
} }
var term256Regex = regexp.MustCompile("(?i)-256(color)?$") var term256Regex = regexp.MustCompile(`(?i)-256(color)?$`)
if term256Regex.MatchString(termenv) { if term256Regex.MatchString(termenv) {
return true return true
} }
var termBasicRegex = regexp.MustCompile("(?i)^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux") var termBasicRegex = regexp.MustCompile(`(?i)^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux`)
if termBasicRegex.MatchString(termenv) { if termBasicRegex.MatchString(termenv) {
return true return true