From 6b8fe4c199844de0be30cab2abf4bb0ced113af5 Mon Sep 17 00:00:00 2001 From: Ajay Kidave Date: Sun, 20 Oct 2024 21:34:14 -0700 Subject: [PATCH] Moved report attr from action to result --- internal/app/action/action.go | 23 ++++++----- internal/app/app.go | 60 +++++++++++++++------------- internal/app/apptype/builtins.go | 21 +++++----- internal/app/setup.go | 12 +----- internal/app/tests/appaction_test.go | 28 ++++++------- 5 files changed, 74 insertions(+), 70 deletions(-) diff --git a/internal/app/action/action.go b/internal/app/action/action.go index cb6eae0..341d495 100644 --- a/internal/app/action/action.go +++ b/internal/app/action/action.go @@ -37,7 +37,6 @@ type Action struct { name string description string path string - report string run starlark.Callable suggest starlark.Callable params []apptype.AppParam @@ -49,7 +48,7 @@ type Action struct { } // NewAction creates a new action -func NewAction(logger *types.Logger, isDev bool, name, description, apath, report string, run, suggest starlark.Callable, +func NewAction(logger *types.Logger, isDev bool, name, description, apath string, run, suggest starlark.Callable, params []apptype.AppParam, paramValuesStr map[string]string, paramDict starlark.StringDict, appPath string) (*Action, error) { @@ -76,7 +75,6 @@ func NewAction(logger *types.Logger, isDev bool, name, description, apath, repor name: name, description: description, path: apath, - report: report, run: run, suggest: suggest, params: params, @@ -204,6 +202,7 @@ func (a *Action) runAction(w http.ResponseWriter, r *http.Request) { var valuesStr []string var status string var paramErrors map[string]any + report := apptype.AUTO resultStruct, ok := ret.(*starlarkstruct.Struct) if ok { @@ -227,6 +226,12 @@ func (a *Action) runAction(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Sprintf("error getting result attr paramErrors: %s", err), http.StatusInternalServerError) return } + + report, err = apptype.GetOptionalStringAttr(resultStruct, "report") + if err != nil { + http.Error(w, fmt.Sprintf("error getting result report: %s", err), http.StatusInternalServerError) + return + } } else { // Not a result struct status = ret.String() @@ -279,19 +284,19 @@ func (a *Action) runAction(w http.ResponseWriter, r *http.Request) { } } - err = a.renderResults(w, valuesMap, valuesStr) + err = a.renderResults(w, report, valuesMap, valuesStr) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } -func (a *Action) renderResults(w http.ResponseWriter, valuesMap []map[string]any, valuesStr []string) error { - if a.report == apptype.AUTO { +func (a *Action) renderResults(w http.ResponseWriter, report string, valuesMap []map[string]any, valuesStr []string) error { + if report == apptype.AUTO { return a.renderResultsAuto(w, valuesMap, valuesStr) } - switch a.report { + switch report { case apptype.TABLE: return a.renderResultsTable(w, valuesMap) case apptype.TEXT: @@ -307,9 +312,9 @@ func (a *Action) renderResults(w http.ResponseWriter, valuesMap []map[string]any } var tmplErr error if len(valuesStr) > 0 { - tmplErr = a.AppTemplate.ExecuteTemplate(w, a.report, valuesStr) + tmplErr = a.AppTemplate.ExecuteTemplate(w, report, valuesStr) } else { - tmplErr = a.AppTemplate.ExecuteTemplate(w, a.report, valuesMap) + tmplErr = a.AppTemplate.ExecuteTemplate(w, report, valuesMap) } _, err = io.WriteString(w, ` `) if err != nil { diff --git a/internal/app/app.go b/internal/app/app.go index 8907811..b8ce2b7 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -68,10 +68,9 @@ type App struct { appRouter *chi.Mux // router for the app actions []*action.Action // actions defined for the app - usesHtmlTemplate bool // Whether the app uses HTML templates, false if only JSON APIs - actionUsesHtmlTemplate bool // Whether the app action uses HTML templates - template *template.Template // unstructured templates, no base_templates defined - templateMap map[string]*template.Template // structured templates, base_templates defined + usesHtmlTemplate bool // Whether the app uses HTML templates, false if only JSON APIs + template *template.Template // unstructured templates, no base_templates defined + templateMap map[string]*template.Template // structured templates, base_templates defined watcher *fsnotify.Watcher sseListeners []chan SSEMessage @@ -288,41 +287,48 @@ func (a *App) Reload(force, immediate bool, dryRun DryRun) (bool, error) { } // Parse HTML templates if there are HTML routes or action uses HTML templates - if a.usesHtmlTemplate || a.actionUsesHtmlTemplate { - baseFiles, err := a.sourceFS.Glob(path.Join(a.codeConfig.Routing.BaseTemplates, "*.go.html")) + baseFiles, err := a.sourceFS.Glob(path.Join(a.codeConfig.Routing.BaseTemplates, "*.go.html")) + if err != nil { + return false, err + } + + if len(baseFiles) == 0 { + // No base templates found, use the default unstructured templates + if a.template, err = a.sourceFS.ParseFS(a.funcMap, a.codeConfig.Routing.TemplateLocations...); err != nil { + if strings.Contains(err.Error(), "pattern matches no files") { + if a.usesHtmlTemplate { + // No html templates found, but app has html routes + return false, err + } + // no html templates, ignore error + } else { + // Some other error parsing templates, report + return false, err + } + } + } else { + // Base templates found, using structured templates + base, err := a.sourceFS.ParseFS(a.funcMap, baseFiles...) if err != nil { return false, err } - if len(baseFiles) == 0 { - // No base templates found, use the default unstructured templates - if a.template, err = a.sourceFS.ParseFS(a.funcMap, a.codeConfig.Routing.TemplateLocations...); err != nil { - return false, err - } - } else { - // Base templates found, using structured templates - base, err := a.sourceFS.ParseFS(a.funcMap, baseFiles...) + a.templateMap = make(map[string]*template.Template) + for _, paths := range a.codeConfig.Routing.TemplateLocations { + files, err := a.sourceFS.Glob(paths) if err != nil { return false, err } - a.templateMap = make(map[string]*template.Template) - for _, paths := range a.codeConfig.Routing.TemplateLocations { - files, err := a.sourceFS.Glob(paths) + for _, file := range files { + tmpl, err := base.Clone() if err != nil { return false, err } - for _, file := range files { - tmpl, err := base.Clone() - if err != nil { - return false, err - } - - a.templateMap[file], err = tmpl.ParseFS(a.sourceFS.ReadableFS, file) - if err != nil { - return false, err - } + a.templateMap[file], err = tmpl.ParseFS(a.sourceFS.ReadableFS, file) + if err != nil { + return false, err } } } diff --git a/internal/app/apptype/builtins.go b/internal/app/apptype/builtins.go index 8aa7fd5..d19122c 100644 --- a/internal/app/apptype/builtins.go +++ b/internal/app/apptype/builtins.go @@ -283,23 +283,18 @@ func createLibraryBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlark } func createActionBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var name, desc, path, report starlark.String + var name, desc, path starlark.String var suggest, executor starlark.Callable - if err := starlark.UnpackArgs(LIBRARY, args, kwargs, "name", &name, "path", &path, - "run", &executor, "suggest?", &suggest, "report?", &report, "description?", &desc); err != nil { + if err := starlark.UnpackArgs(ACTION, args, kwargs, "name", &name, "path", &path, + "run", &executor, "suggest?", &suggest, "description?", &desc); err != nil { return nil, fmt.Errorf("error unpacking action args: %w", err) } - if report == "" { - report = AUTO - } - fields := starlark.StringDict{ "name": name, "description": desc, "path": path, "run": executor, - "report": report, } if suggest != nil { @@ -309,13 +304,18 @@ func createActionBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlark. } func createResultBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var status starlark.String + var status, report starlark.String var values *starlark.List var paramErrors *starlark.Dict - if err := starlark.UnpackArgs(RESPONSE, args, kwargs, "status?", &status, "values?", &values, "param_errors?", ¶mErrors); err != nil { + if err := starlark.UnpackArgs(RESULT, args, kwargs, "status?", &status, "values?", &values, + "report?", &report, "param_errors?", ¶mErrors); err != nil { return nil, fmt.Errorf("error unpacking result args: %w", err) } + if report == "" { + report = AUTO + } + if values == nil { values = starlark.NewList([]starlark.Value{}) } @@ -327,6 +327,7 @@ func createResultBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlark. fields := starlark.StringDict{ "status": status, "values": values, + "report": report, "param_errors": paramErrors, } return starlarkstruct.FromStringDict(starlark.String(RESULT), fields), nil diff --git a/internal/app/setup.go b/internal/app/setup.go index 86b1967..9c71a1a 100644 --- a/internal/app/setup.go +++ b/internal/app/setup.go @@ -461,7 +461,7 @@ func (a *App) addAction(count int, val starlark.Value, router *chi.Mux) error { return fmt.Errorf("actions entry %d is not a struct", count) } - var name, path, description, report string + var name, path, description string var run, suggest starlark.Callable var err error if name, err = apptype.GetStringAttr(actionDef, "name"); err != nil { @@ -476,14 +476,6 @@ func (a *App) addAction(count int, val starlark.Value, router *chi.Mux) error { if run, err = apptype.GetCallableAttr(actionDef, "run"); err != nil { return err } - if report, err = apptype.GetStringAttr(actionDef, "report"); err != nil { - return err - } - - if report != apptype.AUTO && report != apptype.TEXT && report != apptype.TABLE && report != apptype.JSON { - // action is using a custom report, HTML templates are to be parsed - a.actionUsesHtmlTemplate = true - } sa, _ := actionDef.Attr("suggest") if sa != nil { @@ -495,7 +487,7 @@ func (a *App) addAction(count int, val starlark.Value, router *chi.Mux) error { if !strings.HasPrefix(path, "/") { path = "/" + path } - action, err := action.NewAction(a.Logger, a.IsDev, name, description, path, report, run, suggest, + action, err := action.NewAction(a.Logger, a.IsDev, name, description, path, run, suggest, slices.Collect(maps.Values(a.paramInfo)), a.paramValuesStr, a.paramDict, a.Path) if err != nil { return fmt.Errorf("error creating action %s: %w", name, err) diff --git a/internal/app/tests/appaction_test.go b/internal/app/tests/appaction_test.go index 8d2075b..d82406e 100644 --- a/internal/app/tests/appaction_test.go +++ b/internal/app/tests/appaction_test.go @@ -17,7 +17,7 @@ func actionTester(t *testing.T, rootPath bool, actionPath string) { fileData := map[string]string{ "app.star": ` def handler(dry_run, args): - return ace.result(status="done", values=["a", "b"]) + return ace.result(status="done", values=["a", "b"], report=ace.TEXT) app = ace.app("testApp", actions=[ace.action("testAction", "` + actionPath + `", handler)]) @@ -177,7 +177,7 @@ def handler(dry_run, args): return ace.result(status="done", values=[{"a": 1, "b": "abc"}]) app = ace.app("testApp", - actions=[ace.action("testAction", "/", handler, report=ace.AUTO)]) + actions=[ace.action("testAction", "/", handler)]) `, "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")`, @@ -234,10 +234,10 @@ func TestAutoReportJSON(t *testing.T) { fileData := map[string]string{ "app.star": ` def handler(dry_run, args): - return ace.result(status="done", values=[{"a": {"c": 1}, "b": "abc"}]) + return ace.result(status="done", values=[{"a": {"c": 1}, "b": "abc"}], report=ace.AUTO) app = ace.app("testApp", - actions=[ace.action("testAction", "/", handler, report=ace.AUTO)]) + actions=[ace.action("testAction", "/", handler)]) `, "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")`, @@ -287,10 +287,10 @@ func TestReportTable(t *testing.T) { fileData := map[string]string{ "app.star": ` def handler(dry_run, args): - return ace.result(status="done", values=[{"a": {"c": 1}, "b": "abc"}]) + return ace.result(status="done", values=[{"a": {"c": 1}, "b": "abc"}], report=ace.TABLE) app = ace.app("testApp", - actions=[ace.action("testAction", "/", handler, report=ace.TABLE)]) + actions=[ace.action("testAction", "/", handler)]) `, "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")`, @@ -350,10 +350,10 @@ func TestReportTableMissingData(t *testing.T) { fileData := map[string]string{ "app.star": ` def handler(dry_run, args): - return ace.result(status="done", values=[{"a": 1, "b": "abc"}, {"c": 1, "b": "abc2"}]) + return ace.result(status="done", values=[{"a": 1, "b": "abc"}, {"c": 1, "b": "abc2"}], report=ace.TABLE) app = ace.app("testApp", - actions=[ace.action("testAction", "/", handler, report=ace.TABLE)]) + actions=[ace.action("testAction", "/", handler)]) `, "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")`, @@ -415,10 +415,10 @@ func TestParamPost(t *testing.T) { fileData := map[string]string{ "app.star": ` def handler(dry_run, args): - return ace.result(status="done", values=[{"c1": args.param1, "c2": args.param2, "c3": args.param3}]) + return ace.result(status="done", values=[{"c1": args.param1, "c2": args.param2, "c3": args.param3}], report=ace.TABLE) app = ace.app("testApp", - actions=[ace.action("testAction", "/", handler, report=ace.TABLE)]) + actions=[ace.action("testAction", "/", handler)]) `, "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue") @@ -503,10 +503,10 @@ func TestCustomReport(t *testing.T) { fileData := map[string]string{ "app.star": ` def handler(dry_run, args): - return ace.result(status="done", values=[{"a": 1, "b": "abc"}]) + return ace.result(status="done", values=[{"a": 1, "b": "abc"}], report="custom") app = ace.app("testApp", - actions=[ace.action("testAction", "/", handler, report="custom")]) + actions=[ace.action("testAction", "/", handler)]) `, "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")`, @@ -610,10 +610,10 @@ def handler(dry_run, args): if args.param1 == "error": return "errormessage" 10/args.param3 - return ace.result(status="done", values=[{"c1": args.param1, "c2": args.param2, "c3": args.param3}]) + return ace.result(status="done", values=[{"c1": args.param1, "c2": args.param2, "c3": args.param3}], report=ace.TABLE) app = ace.app("testApp", - actions=[ace.action("testAction", "/", handler, report=ace.TABLE)]) + actions=[ace.action("testAction", "/", handler)]) `, "params.star": `param("param1", description="param1 description", type=STRING, default="myvalue")