From 7e7702a8007049fd5eb0ea38b73408f45412a7d4 Mon Sep 17 00:00:00 2001 From: Sven Walter Date: Fri, 19 Jul 2024 11:13:58 +0200 Subject: [PATCH] dump --- migrations.md | 2 +- pkg/webutil/auth.go | 13 +++++ pkg/webutil/view.go | 7 ++- pkg/webutil/viewgo.go | 43 ++++++++++++++++ pkg/webutil/{jetview.go => viewjet.go} | 67 +++---------------------- pkg/webutil/viewjetoptions.go | 69 ++++++++++++++++++++++++++ 6 files changed, 135 insertions(+), 66 deletions(-) create mode 100644 pkg/webutil/viewgo.go rename pkg/webutil/{jetview.go => viewjet.go} (54%) create mode 100644 pkg/webutil/viewjetoptions.go diff --git a/migrations.md b/migrations.md index d1aef88..df00aff 100644 --- a/migrations.md +++ b/migrations.md @@ -270,7 +270,7 @@ The builtin template engine has some shortcommings like being able to pass multi ### Hints * The handler function signatures changed from `func(*webutil.View, *http.Request) webutil.Response` to `func(*http.Request) webutil.Response`. -* Wrap function changes from `webutil.ViewHandler.Wrap` to `webutil.Wrap`. +* Wrap function changes from `webutil.ViewHandler.Wrap` to `webutil.WrapView`. * Since the `View` interface is not part of the function signature, it needs to be added to the struct where the handler function is attached to. * Non-HTML responses are now created with functions directly from the `webutil` package. For example `return webutilext.ViewError(http.StatusInternalServerError, err)` instead of `return v.Error(http.StatusBadRequest, fmt.Errorf(`unknown value for "until"`))`. where `v` was passed to the handler. diff --git a/pkg/webutil/auth.go b/pkg/webutil/auth.go index dd9e62e..4435905 100644 --- a/pkg/webutil/auth.go +++ b/pkg/webutil/auth.go @@ -312,6 +312,8 @@ func DevAuthMiddleware(roles ...string) func(http.Handler) http.Handler { // {{ else }} // Login // {{ end }} +// +// Deprecated: use AuthJetOptions func AuthTemplateFunctions(r *http.Request) template.FuncMap { authenticated := true info := AuthInfoFromRequest(r) @@ -329,6 +331,17 @@ func AuthTemplateFunctions(r *http.Request) template.FuncMap { } } +func AuthJetOptions() JetOption { + return JetOptions( + WithRequestVar("AuthIsAuthenticated", func(r *http.Request) bool { + return AuthInfoFromRequest(r) != nil + }), + WithRequestVar("AuthInfo", func(r *http.Request) *AuthInfo { + return AuthInfoFromRequest(r) + }), + ) +} + // AuthInfoFromContext extracts the AuthInfo from the given context. The // AuthInfo is injected into the request via the AuthMiddleware. Therefore it // is required to use this middleware to be able to get the AuthInfo. diff --git a/pkg/webutil/view.go b/pkg/webutil/view.go index b1598d3..3dfbbed 100644 --- a/pkg/webutil/view.go +++ b/pkg/webutil/view.go @@ -12,13 +12,13 @@ import ( "github.com/sirupsen/logrus" ) -// Deprecated: Use webutil.JetViewer +// Deprecated: Use webutil.JetViewer or webutil.GoTemplateViewer type ViewHandler struct { FS fs.FS FuncMaps []TemplateFuncMap } -// Deprecated: Use webutil.JetViewer +// Deprecated: Use webutil.JetViewer or webutil.GoTemplateViewer func NewViewHandler(fs fs.FS, fms ...TemplateFuncMap) *ViewHandler { v := &ViewHandler{ FS: fs, @@ -28,7 +28,7 @@ func NewViewHandler(fs fs.FS, fms ...TemplateFuncMap) *ViewHandler { return v } -// Deprecated: Use webutil.JetViewer +// Deprecated: Use webutil.JetViewer or webutil.GoTemplateViewer type ResponseHandlerFunc func(*View, *http.Request) Response func (h *ViewHandler) Wrap(fn ResponseHandlerFunc) http.HandlerFunc { @@ -55,7 +55,6 @@ func (h *ViewHandler) Render(filename string, r *http.Request, d interface{}) (* return buf, errors.Wrap(err, "executing template failed") } -// Deprecated type TemplateFuncMap func(*http.Request) template.FuncMap func SimpleTemplateFuncMap(name string, fn interface{}) TemplateFuncMap { diff --git a/pkg/webutil/viewgo.go b/pkg/webutil/viewgo.go new file mode 100644 index 0000000..6bc82f8 --- /dev/null +++ b/pkg/webutil/viewgo.go @@ -0,0 +1,43 @@ +package webutil + +import ( + "bytes" + "html/template" + "io/fs" + "net/http" + + "github.com/pkg/errors" +) + +type GoTemplateViewer struct { + fs fs.FS + funcMaps []TemplateFuncMap +} + +func NewGoTemplateViewer(fs fs.FS, fms ...TemplateFuncMap) *GoTemplateViewer { + return &GoTemplateViewer{ + FS: fs, + FuncMaps: fms, + } +} + +func (v *GoTemplateViewer) HTML(status int, filename string, data any) http.HandlerFunc { +} + +func (v *GoTemplateViewer) Render(filename string, r *http.Request, data any) (*bytes.Buffer, error) { + t := template.New(filename) + + for _, fm := range h.FuncMaps { + t = t.Funcs(fm(r)) + } + + t, err := t.ParseFS(h.FS, "*") + if err != nil { + return nil, errors.Wrap(err, "parsing template failed") + } + + buf := new(bytes.Buffer) + err = t.Execute(buf, data) + + return buf, errors.Wrap(err, "executing template failed") +} diff --git a/pkg/webutil/jetview.go b/pkg/webutil/viewjet.go similarity index 54% rename from pkg/webutil/jetview.go rename to pkg/webutil/viewjet.go index 6e50142..06f0357 100644 --- a/pkg/webutil/jetview.go +++ b/pkg/webutil/viewjet.go @@ -1,7 +1,6 @@ package webutil import ( - "fmt" "io" "io/fs" "net/http" @@ -14,42 +13,14 @@ import ( ) type JetViewer struct { - views *jet.Set - htmlOptions []JetViewerHTMLOption -} - -type JetOption func(*JetViewer) - -func WithJetViewerHTMLOption(o JetViewerHTMLOption) JetOption { - return func(jet *JetViewer) { - jet.htmlOptions = append(jet.htmlOptions, o) - } -} - -func JetFunctionOption(name string, fn any) JetOption { - return func(jet *JetViewer) { - jet.views.AddGlobal(name, fn) - } -} - -// deprecated: use JetFunctionOption -func JetFunctionMapOption(funcs map[string]any) JetOption { - return func(jet *JetViewer) { - for name, fn := range funcs { - jet.views.AddGlobal(name, fn) - } - } -} - -func JetVarOption(key string, value any) JetOption { - return func(jet *JetViewer) { - jet.views.AddGlobal(key, value) - } + views *jet.Set + options []JetOption } func NewJetViewer(js *jet.Set, options ...JetOption) *JetViewer { jv := &JetViewer{ - views: js, + views: js, + options: options, } jv.views.AddGlobal("contains", strings.Contains) @@ -64,36 +35,10 @@ func NewJetViewer(js *jet.Set, options ...JetOption) *JetViewer { return v }) - jv.apply(options...) - return jv } -func (j *JetViewer) apply(options ...JetOption) { - for _, option := range options { - option(j) - } -} - -type JetViewerHTMLOption func(*http.Request, *jet.VarMap) - -func WithVar(name string, value any) JetViewerHTMLOption { - return func(_ *http.Request, vars *jet.VarMap) { - vars.Set(name, value) - } -} - -func WithRequestVar(name string, fn func(*http.Request) any) JetViewerHTMLOption { - return func(r *http.Request, vars *jet.VarMap) { - vars.Set(name, fn(r)) - } -} - -func WithVarf(name string, s string, a ...any) JetViewerHTMLOption { - return WithVar(name, fmt.Sprintf(s, a...)) -} - -func (j *JetViewer) HTML(status int, filename string, data any, opts ...JetViewerHTMLOption) http.HandlerFunc { +func (j *JetViewer) HTML(status int, filename string, data any, opts ...JetOption) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { span, ctx := tracer.StartSpanFromContext( r.Context(), "render", @@ -113,7 +58,7 @@ func (j *JetViewer) HTML(status int, filename string, data any, opts ...JetViewe vars := make(jet.VarMap) vars.Set("currentURLPath", r.URL.Path) - for _, o := range j.htmlOptions { + for _, o := range j.options { o(r, &vars) } diff --git a/pkg/webutil/viewjetoptions.go b/pkg/webutil/viewjetoptions.go new file mode 100644 index 0000000..a584e9e --- /dev/null +++ b/pkg/webutil/viewjetoptions.go @@ -0,0 +1,69 @@ +package webutil + +import ( + "fmt" + "net/http" + + "github.com/CloudyKit/jet/v6" +) + +type JetOption func(*http.Request, *jet.VarMap) + +func JetOptions(options ...JetOption) JetOption { + return func(r *http.Request, m *jet.VarMap) { + for _, option := range options { + option(r, m) + } + } +} + +func WithVar(name string, value any) JetOption { + return func(_ *http.Request, m *jet.VarMap) { + m.Set(name, value) + } +} + +func WithVarf(name string, s string, a ...any) JetOption { + return WithVar(name, fmt.Sprintf(s, a...)) +} + +// WithRequestVar is a JetOption that adds any variable or function to the +// template context, that is based on the *http.Request. +func WithRequestVar[O any](name string, fn func(*http.Request) O) JetOption { + return func(r *http.Request, m *jet.VarMap) { + m.Set(name, fn(r)) + } +} + +// WithRequestVar1 is a shortcut to define a template function with one +// argument that depends on the *http.Request. It simply avoids nesting two +// `return func...` by merging their arguments. +func WithRequestVar1[I1, O any](name string, fn func(*http.Request, I1) O) JetOption { + return WithRequestVar(name, func(r *http.Request) any { + return func(v1 I1) any { + return fn(r, v1) + } + }) +} + +// WithRequestVar2 is a shortcut to define a template function with two +// argument that depends on the *http.Request. It simply avoids nesting two +// `return func...` by merging their arguments. +func WithRequestVar2[I1, I2, O any](name string, fn func(*http.Request, I1, I2) O) JetOption { + return WithRequestVar(name, func(r *http.Request) any { + return func(v1 I1, v2 I2) any { + return fn(r, v1, v2) + } + }) +} + +// WithRequestVar3 is a shortcut to define a template function with three +// argument that depends on the *http.Request. It simply avoids nesting two +// `return func...` by merging their arguments. +func WithRequestVar3[I1, I2, I3, O any](name string, fn func(*http.Request, I1, I2, I3) O) JetOption { + return WithRequestVar(name, func(r *http.Request) any { + return func(v1 I1, v2 I2, v3 I3) any { + return fn(r, v1, v2, v3) + } + }) +}