# HG changeset patch # User Christophe de Vienne <christophe@cdevienne.info> # Date 1743088564 -3600 # Thu Mar 27 16:16:04 2025 +0100 # Node ID 27128e5c0b240884f85a28ea13ce2d00e44fd5a4 # Parent 2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8 api: return friendly errors on template loading diff --git a/engines/mustache/engine.go b/engines/mustache/engine.go --- a/engines/mustache/engine.go +++ b/engines/mustache/engine.go @@ -2,11 +2,13 @@ import ( "context" + "fmt" "reflect" "github.com/orus-io/mustache" "github.com/rs/zerolog" + redner "orus.io/orus-io/rednerd/lib" "orus.io/orus-io/rednerd/models" "orus.io/orus-io/rednerd/rendering" ) @@ -32,7 +34,7 @@ bodyTemplate, err := mustache.ParseString(s) if err != nil { - return nil, err + return nil, fmt.Errorf("%w: %w", redner.ErrInvalidInput, err) } t := Template{ @@ -45,7 +47,7 @@ for name, meta := range template.Metadata { tmpl, err := mustache.ParseString(meta) if err != nil { - return nil, err + return nil, fmt.Errorf("%w: metadata %s: %w", redner.ErrInvalidInput, name, err) } t.metadataTemplates[name] = tmpl diff --git a/lib/render.go b/lib/render.go --- a/lib/render.go +++ b/lib/render.go @@ -187,6 +187,10 @@ ctx, r.registry, template, metadata, toType, ) if err != nil { + if errors.Is(err, ErrInvalidInput) { + return nil, models.NewRenderFailedError(err, nil) + } + if errors.Is(err, rendering.ErrUnknownTemplateLanguage) { return nil, fmt.Errorf("%w: unknown template language: %s", ErrInvalidInput, template.Language) } diff --git a/tests/render_test.go b/tests/render_test.go --- a/tests/render_test.go +++ b/tests/render_test.go @@ -22,6 +22,31 @@ tester.CreateUser("janedoe", "janedoe") tester.Login("janedoe", "janedoe") + t.Run("Mustache-parse-errors", func(t *testing.T) { + defer tester.SetT(t)() + + var result models.RenderFailedError + + tester.RenderExpectingError( + &models.RenderRequest{ + Accept: "text/html", + Template: &models.Template{ + Language: "mustache", + Produces: "text/plain", + BodyFormat: "text", + Body: "{{#toto}} {{/titi}}", + }, + Data: models.Dataset{ + apitester.JSONObj{"name": "Nath"}, + }, + }, + &result, + ) + + assert.Equal(t, "invalid input: line 1: interleaved closing tag: titi", result.Message) + assert.Len(t, result.Details, 0) + }) + t.Run("MJML-Template-to-html", func(t *testing.T) { defer tester.SetT(t)() diff --git a/testutils/apitester/apitester_template_management.go b/testutils/apitester/apitester_template_management.go --- a/testutils/apitester/apitester_template_management.go +++ b/testutils/apitester/apitester_template_management.go @@ -5,6 +5,7 @@ "net/http" "github.com/k3a/html2text" + "github.com/steinfletcher/apitest" "github.com/stretchr/testify/require" "orus.io/orus-io/rednerd/models" @@ -48,23 +49,39 @@ r.JSON(response) } +func (tester *APITester) renderRequest( + data *models.RenderRequest, +) *apitest.Response { + apitest := tester.APITest("render") + if tester.debug { + apitest = apitest.Debug() + } + + return apitest. + Post("/api/v1/render/"). + JSON(data). + Expect(tester.t) +} + // Render renders some data and returns a document. func (tester *APITester) Render( data *models.RenderRequest, result *[]*models.Document, ) { - apitest := tester.APITest("render") - if tester.debug { - apitest = apitest.Debug() - } + tester.renderRequest(data). + Status(http.StatusOK). + End(). + JSON(result) +} - r := apitest. - Post("/api/v1/render/"). - JSON(data). - Expect(tester.t). - Status(http.StatusOK). - End() - r.JSON(result) +func (tester *APITester) RenderExpectingError( + data *models.RenderRequest, + err *models.RenderFailedError, +) { + tester.renderRequest(data). + Status(http.StatusBadRequest). + End(). + JSON(err) } // Renderlogs gives us a csv document containing the