From 059a9f726fc35756614f51a3081b6825f4b3abbd Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 25 Jan 2025 16:09:17 +0100 Subject: [PATCH 01/16] update statuscomponent to handle no render declared --- gno.land/pkg/gnoweb/components/view_status.go | 34 +++++++++++++++++-- .../pkg/gnoweb/components/views/status.html | 17 ++++++++-- gno.land/pkg/gnoweb/handler.go | 18 ++++++---- gno.land/pkg/gnoweb/webclient.go | 1 + gno.land/pkg/gnoweb/webclient_html.go | 5 +++ 5 files changed, 62 insertions(+), 13 deletions(-) diff --git a/gno.land/pkg/gnoweb/components/view_status.go b/gno.land/pkg/gnoweb/components/view_status.go index 46f998c45cb..76996fe9d38 100644 --- a/gno.land/pkg/gnoweb/components/view_status.go +++ b/gno.land/pkg/gnoweb/components/view_status.go @@ -2,10 +2,38 @@ package components const StatusViewType ViewType = "status-view" +// StatusData holds the dynamic fields for the "status" template type StatusData struct { - Message string + Title string + Body string + ButtonURL string + ButtonText string } -func StatusComponent(message string) *View { - return NewTemplateView(StatusViewType, "status", StatusData{message}) +// StatusErrorComponent returns a view for error scenarios +func StatusErrorComponent(message string) *View { + return NewTemplateView( + StatusViewType, + "status", + StatusData{ + Title: "Error", + Body: message, + ButtonURL: "/", + ButtonText: "Go Back Home", + }, + ) +} + +// StatusNoRenderComponent returns a view for non-error notifications +func StatusNoRenderComponent(pkgPath string) *View { + return NewTemplateView( + StatusViewType, + "status", + StatusData{ + Title: "No Render", + Body: "The developer of this realm did not create a Render() function.", + ButtonURL: pkgPath + "$source", + ButtonText: "View Realm Source", + }, + ) } diff --git a/gno.land/pkg/gnoweb/components/views/status.html b/gno.land/pkg/gnoweb/components/views/status.html index ab068cbf7e4..8f23c6c4057 100644 --- a/gno.land/pkg/gnoweb/components/views/status.html +++ b/gno.land/pkg/gnoweb/components/views/status.html @@ -1,8 +1,19 @@ {{ define "status" }}
+ gno land -

Error: {{ .Message }}

-

Something went wrong. Let’s find our way back!

- Go Back Home + + +

+ {{ .Title }} +

+ + +

{{ .Body }}

+ + + + {{ .ButtonText }} +
{{ end }} diff --git a/gno.land/pkg/gnoweb/handler.go b/gno.land/pkg/gnoweb/handler.go index 3fdfc33909c..acce6c57b3c 100644 --- a/gno.land/pkg/gnoweb/handler.go +++ b/gno.land/pkg/gnoweb/handler.go @@ -112,7 +112,7 @@ func (h *WebHandler) prepareIndexBodyView(r *http.Request, indexData *components gnourl, err := ParseGnoURL(r.URL) if err != nil { h.Logger.Warn("unable to parse url path", "path", r.URL.Path, "error", err) - return http.StatusNotFound, components.StatusComponent("invalid path") + return http.StatusNotFound, components.StatusErrorComponent("invalid path") } breadcrumb := generateBreadcrumbPaths(gnourl) @@ -128,7 +128,7 @@ func (h *WebHandler) prepareIndexBodyView(r *http.Request, indexData *components return h.GetPackageView(gnourl) default: h.Logger.Debug("invalid path: path is neither a pure package or a realm") - return http.StatusBadRequest, components.StatusComponent("invalid path") + return http.StatusBadRequest, components.StatusErrorComponent("invalid path") } } @@ -158,6 +158,10 @@ func (h *WebHandler) GetRealmView(gnourl *GnoURL) (int, *components.View) { meta, err := h.Client.RenderRealm(&content, gnourl.Path, gnourl.EncodeArgs()) if err != nil { + if errors.Is(err, ErrRenderNotDeclared) { + return http.StatusOK, components.StatusNoRenderComponent(gnourl.Path) + } + h.Logger.Error("unable to render realm", "error", err, "path", gnourl.EncodeURL()) return GetClientErrorStatusPage(gnourl, err) } @@ -221,7 +225,7 @@ func (h *WebHandler) GetSourceView(gnourl *GnoURL) (int, *components.View) { if len(files) == 0 { h.Logger.Debug("no files available", "path", gnourl.Path) - return http.StatusOK, components.StatusComponent("no files available") + return http.StatusOK, components.StatusErrorComponent("no files available") } var fileName string @@ -264,7 +268,7 @@ func (h *WebHandler) GetDirectoryView(gnourl *GnoURL) (int, *components.View) { if len(files) == 0 { h.Logger.Debug("no files available", "path", gnourl.Path) - return http.StatusOK, components.StatusComponent("no files available") + return http.StatusOK, components.StatusErrorComponent("no files available") } return http.StatusOK, components.DirectoryView(components.DirData{ @@ -281,13 +285,13 @@ func GetClientErrorStatusPage(_ *GnoURL, err error) (int, *components.View) { switch { case errors.Is(err, ErrClientPathNotFound): - return http.StatusNotFound, components.StatusComponent(err.Error()) + return http.StatusNotFound, components.StatusErrorComponent(err.Error()) case errors.Is(err, ErrClientBadRequest): - return http.StatusInternalServerError, components.StatusComponent("bad request") + return http.StatusInternalServerError, components.StatusErrorComponent("bad request") case errors.Is(err, ErrClientResponse): fallthrough // XXX: for now fallback as internal error default: - return http.StatusInternalServerError, components.StatusComponent("internal error") + return http.StatusInternalServerError, components.StatusErrorComponent("internal error") } } diff --git a/gno.land/pkg/gnoweb/webclient.go b/gno.land/pkg/gnoweb/webclient.go index de44303f352..a2f01ec7f48 100644 --- a/gno.land/pkg/gnoweb/webclient.go +++ b/gno.land/pkg/gnoweb/webclient.go @@ -10,6 +10,7 @@ import ( var ( ErrClientPathNotFound = errors.New("package not found") + ErrRenderNotDeclared = errors.New("realm has no Render function defined") ErrClientBadRequest = errors.New("bad request") ErrClientResponse = errors.New("node response error") ) diff --git a/gno.land/pkg/gnoweb/webclient_html.go b/gno.land/pkg/gnoweb/webclient_html.go index d856c6f87a0..9141f275ebe 100644 --- a/gno.land/pkg/gnoweb/webclient_html.go +++ b/gno.land/pkg/gnoweb/webclient_html.go @@ -177,6 +177,7 @@ func (s *HTMLWebClient) RenderRealm(w io.Writer, pkgPath string, args string) (* pkgPath = strings.Trim(pkgPath, "/") data := fmt.Sprintf("%s/%s:%s", s.domain, pkgPath, args) + rawres, err := s.query(qpath, []byte(data)) if err != nil { return nil, err @@ -213,6 +214,10 @@ func (s *HTMLWebClient) query(qpath string, data []byte) ([]byte, error) { return nil, ErrClientPathNotFound } + if strings.Contains(err.Error(), "Render not declared") { + return nil, ErrRenderNotDeclared + } + s.logger.Error("response error", "path", qpath, "log", qres.Response.Log) return nil, fmt.Errorf("%w: %s", ErrClientResponse, err.Error()) } From 870a27bf8de595b098fcf85f41a017bc94fcf452 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 25 Jan 2025 16:16:51 +0100 Subject: [PATCH 02/16] err --- gno.land/pkg/gnoweb/webclient.go | 2 +- gno.land/pkg/gnoweb/webclient_html.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gno.land/pkg/gnoweb/webclient.go b/gno.land/pkg/gnoweb/webclient.go index a2f01ec7f48..b847a51f936 100644 --- a/gno.land/pkg/gnoweb/webclient.go +++ b/gno.land/pkg/gnoweb/webclient.go @@ -10,7 +10,7 @@ import ( var ( ErrClientPathNotFound = errors.New("package not found") - ErrRenderNotDeclared = errors.New("realm has no Render function defined") + ErrRenderNotDeclared = errors.New("render function not declared") ErrClientBadRequest = errors.New("bad request") ErrClientResponse = errors.New("node response error") ) diff --git a/gno.land/pkg/gnoweb/webclient_html.go b/gno.land/pkg/gnoweb/webclient_html.go index 9141f275ebe..3bfb8d3701c 100644 --- a/gno.land/pkg/gnoweb/webclient_html.go +++ b/gno.land/pkg/gnoweb/webclient_html.go @@ -214,7 +214,7 @@ func (s *HTMLWebClient) query(qpath string, data []byte) ([]byte, error) { return nil, ErrClientPathNotFound } - if strings.Contains(err.Error(), "Render not declared") { + if strings.Contains(err.Error(), "Render not declared") { /// XXX shoud this be a vm error? return nil, ErrRenderNotDeclared } From 480a1afb3062cbeb12b5179cde2313ecaa7f5a5f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 25 Jan 2025 16:27:43 +0100 Subject: [PATCH 03/16] fix old errors --- gno.land/pkg/gnoweb/components/view_status.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gno.land/pkg/gnoweb/components/view_status.go b/gno.land/pkg/gnoweb/components/view_status.go index 76996fe9d38..56477a4db0a 100644 --- a/gno.land/pkg/gnoweb/components/view_status.go +++ b/gno.land/pkg/gnoweb/components/view_status.go @@ -16,8 +16,8 @@ func StatusErrorComponent(message string) *View { StatusViewType, "status", StatusData{ - Title: "Error", - Body: message, + Title: "Error: " + message, + Body: "Something went wrong.", ButtonURL: "/", ButtonText: "Go Back Home", }, @@ -31,7 +31,7 @@ func StatusNoRenderComponent(pkgPath string) *View { "status", StatusData{ Title: "No Render", - Body: "The developer of this realm did not create a Render() function.", + Body: "This realm does not implement a Render() function.", ButtonURL: pkgPath + "$source", ButtonText: "View Realm Source", }, From 7643160b1a46762f8e67f890df47ce46e8d87f1e Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 25 Jan 2025 16:28:22 +0100 Subject: [PATCH 04/16] rm cmts --- gno.land/pkg/gnoweb/components/views/status.html | 7 ------- 1 file changed, 7 deletions(-) diff --git a/gno.land/pkg/gnoweb/components/views/status.html b/gno.land/pkg/gnoweb/components/views/status.html index 8f23c6c4057..f4533275789 100644 --- a/gno.land/pkg/gnoweb/components/views/status.html +++ b/gno.land/pkg/gnoweb/components/views/status.html @@ -1,17 +1,10 @@ {{ define "status" }}
- gno land - -

{{ .Title }}

- -

{{ .Body }}

- - {{ .ButtonText }} From 8d56146842f6ed78875f59ad09e58fb8c36449ff Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 25 Jan 2025 19:33:06 +0100 Subject: [PATCH 05/16] add test --- examples/gno.land/r/docs/optional_render/gno.mod | 1 + .../gno.land/r/docs/optional_render/optional_render.gno | 7 +++++++ gno.land/pkg/gnoweb/app_test.go | 1 + 3 files changed, 9 insertions(+) create mode 100644 examples/gno.land/r/docs/optional_render/gno.mod create mode 100644 examples/gno.land/r/docs/optional_render/optional_render.gno diff --git a/examples/gno.land/r/docs/optional_render/gno.mod b/examples/gno.land/r/docs/optional_render/gno.mod new file mode 100644 index 00000000000..4c8162ca46d --- /dev/null +++ b/examples/gno.land/r/docs/optional_render/gno.mod @@ -0,0 +1 @@ +module gno.land/r/docs/optional_render diff --git a/examples/gno.land/r/docs/optional_render/optional_render.gno b/examples/gno.land/r/docs/optional_render/optional_render.gno new file mode 100644 index 00000000000..77da30609b3 --- /dev/null +++ b/examples/gno.land/r/docs/optional_render/optional_render.gno @@ -0,0 +1,7 @@ +package optional_render + +func Info() string { + return `Having a Render() function in your realm is optional! +If you do decide to have a Render() function, it must have the following signature: +func Render(path string) string { ... }` +} diff --git a/gno.land/pkg/gnoweb/app_test.go b/gno.land/pkg/gnoweb/app_test.go index 6fb69c6d984..19c83e6f1df 100644 --- a/gno.land/pkg/gnoweb/app_test.go +++ b/gno.land/pkg/gnoweb/app_test.go @@ -47,6 +47,7 @@ func TestRoutes(t *testing.T) { {"/game-of-realms", found, "/contribute"}, {"/gor", found, "/contribute"}, {"/blog", found, "/r/gnoland/blog"}, + {"/r/docs/optional_render", ok, "No Render"}, {"/r/not/found/", notFound, ""}, {"/404/not/found", notFound, ""}, {"/아스키문자가아닌경로", notFound, ""}, From 2726aeab1a03bd17a9bfaac5e47ff35174d58ac5 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Sat, 25 Jan 2025 19:36:06 +0100 Subject: [PATCH 06/16] add r/docs ref --- examples/gno.land/r/docs/docs.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/r/docs/docs.gno b/examples/gno.land/r/docs/docs.gno index 28bac4171b5..2564ea13ae3 100644 --- a/examples/gno.land/r/docs/docs.gno +++ b/examples/gno.land/r/docs/docs.gno @@ -14,6 +14,7 @@ Explore various examples to learn more about Gno functionality and usage. - [Buttons](/r/docs/buttons) - Add buttons to your realm's render. - [AVL Pager](/r/docs/avl_pager) - Paginate through AVL tree items. - [Img Embed](/r/docs/img_embed) - Demonstrates how to embed an image. +- [Optional Render](/r/docs/optional_render) - Render() is optional in realms. - ... From 18167fa0c0406626873ae9853afc8f40624eec4a Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 12:57:33 +0100 Subject: [PATCH 07/16] add test --- gno.land/pkg/gnoweb/handler_test.go | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/gno.land/pkg/gnoweb/handler_test.go b/gno.land/pkg/gnoweb/handler_test.go index 624e3390a97..f0e0f8ad25d 100644 --- a/gno.land/pkg/gnoweb/handler_test.go +++ b/gno.land/pkg/gnoweb/handler_test.go @@ -110,3 +110,43 @@ func TestWebHandler_Get(t *testing.T) { }) } } + +// TestWebHandler_NoRender checks if gnoweb displays the `No Render` page properly. +// This happens when the render being queried does not have a Render function declared. +func TestWebHandler_NoRender(t *testing.T) { + t.Parallel() + + mockPath := "/r/mock/path" + mockPackage := &gnoweb.MockPackage{ + Domain: "example.com", + Path: "/r/mock/path", + Files: map[string]string{ + "render.gno": `package main; func init() {}`, + "gno.mod": `module example.com/r/mock/path`, + "LicEnse": `my super license`, + }, + Functions: []vm.FunctionSignature{ + {FuncName: "SuperCoolFunction", Params: []vm.NamedType{ + {Name: "my_super_arg", Type: "string"}, + }}, + }, + } + + webclient := gnoweb.NewMockWebClient(mockPackage) + config := gnoweb.WebHandlerConfig{ + WebClient: webclient, + } + + logger := slog.New(slog.NewTextHandler(&testingLogger{t}, &slog.HandlerOptions{})) + handler, err := gnoweb.NewWebHandler(logger, config) + require.NoError(t, err, "failed to create WebHandler") + + req, err := http.NewRequest(http.MethodGet, mockPath, nil) + require.NoError(t, err, "failed to create HTTP request") + + rr := httptest.NewRecorder() + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusOK, rr.Code, "unexpected status code") + assert.Containsf(t, rr.Body.String(), "", "rendered body should contain: %q", "No Render") +} From 1c92e8f47a7cdedde85941b73013747daf8253ef Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 12:59:10 +0100 Subject: [PATCH 08/16] add condition --- gno.land/pkg/gnoweb/handler_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/gno.land/pkg/gnoweb/handler_test.go b/gno.land/pkg/gnoweb/handler_test.go index f0e0f8ad25d..c72315903c2 100644 --- a/gno.land/pkg/gnoweb/handler_test.go +++ b/gno.land/pkg/gnoweb/handler_test.go @@ -149,4 +149,5 @@ func TestWebHandler_NoRender(t *testing.T) { assert.Equal(t, http.StatusOK, rr.Code, "unexpected status code") assert.Containsf(t, rr.Body.String(), "", "rendered body should contain: %q", "No Render") + assert.Containsf(t, rr.Body.String(), "", "rendered body should contain: %q", "This realm does not implement a Render() function.") } From 8f074e188a76c92f614ab08fc07afac1446904ee Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 15:40:44 +0100 Subject: [PATCH 09/16] rm funcs --- gno.land/pkg/gnoweb/handler_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gno.land/pkg/gnoweb/handler_test.go b/gno.land/pkg/gnoweb/handler_test.go index c72315903c2..a8e3702bc6f 100644 --- a/gno.land/pkg/gnoweb/handler_test.go +++ b/gno.land/pkg/gnoweb/handler_test.go @@ -125,11 +125,6 @@ func TestWebHandler_NoRender(t *testing.T) { "gno.mod": `module example.com/r/mock/path`, "LicEnse": `my super license`, }, - Functions: []vm.FunctionSignature{ - {FuncName: "SuperCoolFunction", Params: []vm.NamedType{ - {Name: "my_super_arg", Type: "string"}, - }}, - }, } webclient := gnoweb.NewMockWebClient(mockPackage) From f76030afadb8311e3acf3dcf7a80151ad01dc5ab Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 21:16:33 +0100 Subject: [PATCH 10/16] add vm error, update mock web client in gnoweb, fix tests --- gno.land/pkg/gnoweb/app_test.go | 2 +- gno.land/pkg/gnoweb/handler.go | 2 +- gno.land/pkg/gnoweb/handler_test.go | 7 +++---- gno.land/pkg/gnoweb/webclient.go | 2 +- gno.land/pkg/gnoweb/webclient_html.go | 2 +- gno.land/pkg/gnoweb/webclient_mock.go | 12 +++++++++++- gno.land/pkg/sdk/vm/errors.go | 2 ++ gno.land/pkg/sdk/vm/handler.go | 5 +++++ gno.land/pkg/sdk/vm/package.go | 1 + 9 files changed, 26 insertions(+), 9 deletions(-) diff --git a/gno.land/pkg/gnoweb/app_test.go b/gno.land/pkg/gnoweb/app_test.go index 19c83e6f1df..eb17ee4d0e9 100644 --- a/gno.land/pkg/gnoweb/app_test.go +++ b/gno.land/pkg/gnoweb/app_test.go @@ -47,7 +47,7 @@ func TestRoutes(t *testing.T) { {"/game-of-realms", found, "/contribute"}, {"/gor", found, "/contribute"}, {"/blog", found, "/r/gnoland/blog"}, - {"/r/docs/optional_render", ok, "No Render"}, + {"/r/docs/optional_render", http.StatusNoContent, "No Render"}, {"/r/not/found/", notFound, ""}, {"/404/not/found", notFound, ""}, {"/아스키문자가아닌경로", notFound, ""}, diff --git a/gno.land/pkg/gnoweb/handler.go b/gno.land/pkg/gnoweb/handler.go index ac39f4ce0f9..822fd50fa1b 100644 --- a/gno.land/pkg/gnoweb/handler.go +++ b/gno.land/pkg/gnoweb/handler.go @@ -161,7 +161,7 @@ func (h *WebHandler) GetRealmView(gnourl *GnoURL) (int, *components.View) { meta, err := h.Client.RenderRealm(&content, gnourl.Path, gnourl.EncodeArgs()) if err != nil { if errors.Is(err, ErrRenderNotDeclared) { - return http.StatusOK, components.StatusNoRenderComponent(gnourl.Path) + return http.StatusNoContent, components.StatusNoRenderComponent(gnourl.Path) } h.Logger.Error("unable to render realm", "error", err, "path", gnourl.EncodeURL()) diff --git a/gno.land/pkg/gnoweb/handler_test.go b/gno.land/pkg/gnoweb/handler_test.go index a8e3702bc6f..1608b2e37ab 100644 --- a/gno.land/pkg/gnoweb/handler_test.go +++ b/gno.land/pkg/gnoweb/handler_test.go @@ -118,12 +118,11 @@ func TestWebHandler_NoRender(t *testing.T) { mockPath := "/r/mock/path" mockPackage := &gnoweb.MockPackage{ - Domain: "example.com", + Domain: "gno.land", Path: "/r/mock/path", Files: map[string]string{ "render.gno": `package main; func init() {}`, - "gno.mod": `module example.com/r/mock/path`, - "LicEnse": `my super license`, + "gno.mod": `module gno.land/r/mock/path`, }, } @@ -142,7 +141,7 @@ func TestWebHandler_NoRender(t *testing.T) { rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code, "unexpected status code") + assert.Equal(t, http.StatusNoContent, rr.Code, "unexpected status code") assert.Containsf(t, rr.Body.String(), "", "rendered body should contain: %q", "No Render") assert.Containsf(t, rr.Body.String(), "", "rendered body should contain: %q", "This realm does not implement a Render() function.") } diff --git a/gno.land/pkg/gnoweb/webclient.go b/gno.land/pkg/gnoweb/webclient.go index b847a51f936..1def3bc3812 100644 --- a/gno.land/pkg/gnoweb/webclient.go +++ b/gno.land/pkg/gnoweb/webclient.go @@ -24,7 +24,7 @@ type RealmMeta struct { Toc md.Toc } -// WebClient is an interface for interacting with package and node ressources. +// WebClient is an interface for interacting with package and node resources. type WebClient interface { // RenderRealm renders the content of a realm from a given path and // arguments into the giver `writer`. The method should ensures the rendered diff --git a/gno.land/pkg/gnoweb/webclient_html.go b/gno.land/pkg/gnoweb/webclient_html.go index 3bfb8d3701c..c04a7f9e457 100644 --- a/gno.land/pkg/gnoweb/webclient_html.go +++ b/gno.land/pkg/gnoweb/webclient_html.go @@ -214,7 +214,7 @@ func (s *HTMLWebClient) query(qpath string, data []byte) ([]byte, error) { return nil, ErrClientPathNotFound } - if strings.Contains(err.Error(), "Render not declared") { /// XXX shoud this be a vm error? + if errors.Is(err, vm.NoRenderDeclError{}) { return nil, ErrRenderNotDeclared } diff --git a/gno.land/pkg/gnoweb/webclient_mock.go b/gno.land/pkg/gnoweb/webclient_mock.go index 451f5e237c3..9452968dad5 100644 --- a/gno.land/pkg/gnoweb/webclient_mock.go +++ b/gno.land/pkg/gnoweb/webclient_mock.go @@ -31,13 +31,23 @@ func NewMockWebClient(pkgs ...*MockPackage) *MockWebClient { return &MockWebClient{Packages: mpkgs} } -// Render simulates rendering a package by writing its content to the writer. +// RenderRealm simulates rendering a package by writing its content to the writer. func (m *MockWebClient) RenderRealm(w io.Writer, path string, args string) (*RealmMeta, error) { pkg, exists := m.Packages[path] if !exists { return nil, ErrClientPathNotFound } + noRender := true + for _, fn := range pkg.Functions { + if fn.FuncName == "Render" { + noRender = false + } + } + if noRender { + return nil, ErrRenderNotDeclared + } + fmt.Fprintf(w, "[%s]%s:", pkg.Domain, pkg.Path) // Return a dummy RealmMeta for simplicity diff --git a/gno.land/pkg/sdk/vm/errors.go b/gno.land/pkg/sdk/vm/errors.go index c8d6da98970..208fb074f7e 100644 --- a/gno.land/pkg/sdk/vm/errors.go +++ b/gno.land/pkg/sdk/vm/errors.go @@ -16,6 +16,7 @@ func (abciError) AssertABCIError() {} // NOTE: these are meant to be used in conjunction with pkgs/errors. type ( InvalidPkgPathError struct{ abciError } + NoRenderDeclError struct{ abciError } PkgExistError struct{ abciError } InvalidStmtError struct{ abciError } InvalidExprError struct{ abciError } @@ -27,6 +28,7 @@ type ( ) func (e InvalidPkgPathError) Error() string { return "invalid package path" } +func (e NoRenderDeclError) Error() string { return "render function not declared" } func (e PkgExistError) Error() string { return "package already exists" } func (e InvalidStmtError) Error() string { return "invalid statement" } func (e InvalidExprError) Error() string { return "invalid expression" } diff --git a/gno.land/pkg/sdk/vm/handler.go b/gno.land/pkg/sdk/vm/handler.go index c484e07e887..e5a1ef7663a 100644 --- a/gno.land/pkg/sdk/vm/handler.go +++ b/gno.land/pkg/sdk/vm/handler.go @@ -128,10 +128,15 @@ func (vh vmHandler) queryRender(ctx sdk.Context, req abci.RequestQuery) (res abc pkgPath, path := reqData[:dot], reqData[dot+1:] expr := fmt.Sprintf("Render(%q)", path) result, err := vh.vm.QueryEvalString(ctx, pkgPath, expr) + if err != nil { + if strings.Contains(err.Error(), "Render not declared") { + err = NoRenderDeclError{} + } res = sdk.ABCIResponseQueryFromError(err) return } + res.Data = []byte(result) return } diff --git a/gno.land/pkg/sdk/vm/package.go b/gno.land/pkg/sdk/vm/package.go index 0359061ccea..95e97648dac 100644 --- a/gno.land/pkg/sdk/vm/package.go +++ b/gno.land/pkg/sdk/vm/package.go @@ -20,6 +20,7 @@ var Package = amino.RegisterPackage(amino.NewPackage( // errors InvalidPkgPathError{}, "InvalidPkgPathError", + NoRenderDeclError{}, "NoRenderDeclError", PkgExistError{}, "PkgExistError", InvalidStmtError{}, "InvalidStmtError", InvalidExprError{}, "InvalidExprError", From 48ef21519620d2faa898abd7b69ade34a408b941 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 21:18:50 +0100 Subject: [PATCH 11/16] fmt --- gno.land/pkg/sdk/vm/handler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/gno.land/pkg/sdk/vm/handler.go b/gno.land/pkg/sdk/vm/handler.go index e5a1ef7663a..5aebf1afe46 100644 --- a/gno.land/pkg/sdk/vm/handler.go +++ b/gno.land/pkg/sdk/vm/handler.go @@ -128,7 +128,6 @@ func (vh vmHandler) queryRender(ctx sdk.Context, req abci.RequestQuery) (res abc pkgPath, path := reqData[:dot], reqData[dot+1:] expr := fmt.Sprintf("Render(%q)", path) result, err := vh.vm.QueryEvalString(ctx, pkgPath, expr) - if err != nil { if strings.Contains(err.Error(), "Render not declared") { err = NoRenderDeclError{} From 548d69c84c77a4ef8da6c883d42d8746e6deae5f Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 21:34:39 +0100 Subject: [PATCH 12/16] fix test --- gno.land/pkg/gnoweb/handler_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gno.land/pkg/gnoweb/handler_test.go b/gno.land/pkg/gnoweb/handler_test.go index 1608b2e37ab..f6c01da45aa 100644 --- a/gno.land/pkg/gnoweb/handler_test.go +++ b/gno.land/pkg/gnoweb/handler_test.go @@ -29,7 +29,7 @@ func TestWebHandler_Get(t *testing.T) { Domain: "example.com", Path: "/r/mock/path", Files: map[string]string{ - "render.gno": `package main; func Render(path string) { return "one more time" }`, + "render.gno": `package main; func Render(path string) (res string) { return "one more time" }`, "gno.mod": `module example.com/r/mock/path`, "LicEnse": `my super license`, }, @@ -37,6 +37,8 @@ func TestWebHandler_Get(t *testing.T) { {FuncName: "SuperRenderFunction", Params: []vm.NamedType{ {Name: "my_super_arg", Type: "string"}, }}, + {FuncName: "Render", Params: []vm.NamedType{{Name: "path", Type: "string"}}, + Results: []vm.NamedType{{Name: "res", Type: "string"}}}, }, } From f1a61bc7d503651ca6994debf53f4b47125165a7 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 21:42:00 +0100 Subject: [PATCH 13/16] clean up code --- gno.land/pkg/gnoweb/handler_test.go | 5 +++-- gno.land/pkg/gnoweb/webclient_mock.go | 26 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/gno.land/pkg/gnoweb/handler_test.go b/gno.land/pkg/gnoweb/handler_test.go index f6c01da45aa..dbf4354f11b 100644 --- a/gno.land/pkg/gnoweb/handler_test.go +++ b/gno.land/pkg/gnoweb/handler_test.go @@ -24,12 +24,13 @@ func (t *testingLogger) Write(b []byte) (n int, err error) { // TestWebHandler_Get tests the Get method of WebHandler using table-driven tests. func TestWebHandler_Get(t *testing.T) { + t.Parallel() // Set up a mock package with some files and functions mockPackage := &gnoweb.MockPackage{ Domain: "example.com", Path: "/r/mock/path", Files: map[string]string{ - "render.gno": `package main; func Render(path string) (res string) { return "one more time" }`, + "render.gno": `package main; func Render(path string) string { return "one more time" }`, "gno.mod": `module example.com/r/mock/path`, "LicEnse": `my super license`, }, @@ -38,7 +39,7 @@ func TestWebHandler_Get(t *testing.T) { {Name: "my_super_arg", Type: "string"}, }}, {FuncName: "Render", Params: []vm.NamedType{{Name: "path", Type: "string"}}, - Results: []vm.NamedType{{Name: "res", Type: "string"}}}, + Results: []vm.NamedType{{Name: "", Type: "string"}}}, }, } diff --git a/gno.land/pkg/gnoweb/webclient_mock.go b/gno.land/pkg/gnoweb/webclient_mock.go index 9452968dad5..cf50943109b 100644 --- a/gno.land/pkg/gnoweb/webclient_mock.go +++ b/gno.land/pkg/gnoweb/webclient_mock.go @@ -38,13 +38,7 @@ func (m *MockWebClient) RenderRealm(w io.Writer, path string, args string) (*Rea return nil, ErrClientPathNotFound } - noRender := true - for _, fn := range pkg.Functions { - if fn.FuncName == "Render" { - noRender = false - } - } - if noRender { + if !pkgHasRender(pkg) { return nil, ErrRenderNotDeclared } @@ -99,3 +93,21 @@ func (m *MockWebClient) Sources(path string) ([]string, error) { return fileNames, nil } + +func pkgHasRender(pkg *MockPackage) bool { + if len(pkg.Functions) == 0 { + return false + } + + for _, fn := range pkg.Functions { + if fn.FuncName == "Render" && + len(fn.Params) == 1 && + len(fn.Results) == 1 && + fn.Params[0].Type == "string" && + fn.Results[0].Type == "string" { + return true + } + } + + return false +} From d37f6beb5888ee98deb7bc28db4f5e24fed80fc0 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Tue, 28 Jan 2025 21:46:10 +0100 Subject: [PATCH 14/16] fmt --- gno.land/pkg/gnoweb/handler_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gno.land/pkg/gnoweb/handler_test.go b/gno.land/pkg/gnoweb/handler_test.go index dbf4354f11b..e85434a6f41 100644 --- a/gno.land/pkg/gnoweb/handler_test.go +++ b/gno.land/pkg/gnoweb/handler_test.go @@ -38,8 +38,10 @@ func TestWebHandler_Get(t *testing.T) { {FuncName: "SuperRenderFunction", Params: []vm.NamedType{ {Name: "my_super_arg", Type: "string"}, }}, - {FuncName: "Render", Params: []vm.NamedType{{Name: "path", Type: "string"}}, - Results: []vm.NamedType{{Name: "", Type: "string"}}}, + { + FuncName: "Render", Params: []vm.NamedType{{Name: "path", Type: "string"}}, + Results: []vm.NamedType{{Name: "", Type: "string"}}, + }, }, } @@ -85,6 +87,7 @@ func TestWebHandler_Get(t *testing.T) { for _, tc := range cases { t.Run(strings.TrimPrefix(tc.Path, "/"), func(t *testing.T) { + t.Parallel() t.Logf("input: %+v", tc) // Initialize testing logger From 2b852187d544d991cc6f3ea257607754a5f512b2 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 29 Jan 2025 10:57:14 +0100 Subject: [PATCH 15/16] rm fmt --- gno.land/pkg/gnoweb/webclient_mock.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/gno.land/pkg/gnoweb/webclient_mock.go b/gno.land/pkg/gnoweb/webclient_mock.go index cf50943109b..344ea262625 100644 --- a/gno.land/pkg/gnoweb/webclient_mock.go +++ b/gno.land/pkg/gnoweb/webclient_mock.go @@ -42,8 +42,6 @@ func (m *MockWebClient) RenderRealm(w io.Writer, path string, args string) (*Rea return nil, ErrRenderNotDeclared } - fmt.Fprintf(w, "[%s]%s:", pkg.Domain, pkg.Path) - // Return a dummy RealmMeta for simplicity return &RealmMeta{}, nil } From 0dbcec9866d0e87c1e2d389841f8ceb1c9ae80c9 Mon Sep 17 00:00:00 2001 From: leohhhn Date: Wed, 29 Jan 2025 11:01:16 +0100 Subject: [PATCH 16/16] return line --- gno.land/pkg/gnoweb/webclient_mock.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gno.land/pkg/gnoweb/webclient_mock.go b/gno.land/pkg/gnoweb/webclient_mock.go index 344ea262625..8a037c181e0 100644 --- a/gno.land/pkg/gnoweb/webclient_mock.go +++ b/gno.land/pkg/gnoweb/webclient_mock.go @@ -42,6 +42,9 @@ func (m *MockWebClient) RenderRealm(w io.Writer, path string, args string) (*Rea return nil, ErrRenderNotDeclared } + // Write to the realm render + fmt.Fprintf(w, "[%s]%s:", pkg.Domain, pkg.Path) + // Return a dummy RealmMeta for simplicity return &RealmMeta{}, nil }