Skip to content

Commit

Permalink
Added plain text response format
Browse files Browse the repository at this point in the history
  • Loading branch information
akclace committed Mar 14, 2024
1 parent 0e0e7df commit e5cd6c2
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 18 deletions.
34 changes: 28 additions & 6 deletions internal/app/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (a *App) createHandlerFunc(html, block string, handler starlark.Callable, r

if a.Config.Routing.EarlyHints && !a.IsDev && r.Method == http.MethodGet &&
r.Header.Get("sec-fetch-mode") == "navigate" &&
!(strings.ToLower(rtype) == "json") && !(isHtmxRequest && block != "") {
!(strings.ToUpper(rtype) == util.JSON) && !(isHtmxRequest && block != "") {
// Prod mode, for a GET request from newer browsers on a top level HTML page, send http early hints
a.earlyHints(w, r)
}
Expand Down Expand Up @@ -213,7 +213,8 @@ func (a *App) createHandlerFunc(html, block string, handler starlark.Callable, r
}
}

if strings.ToLower(rtype) == "json" {
rtype = strings.ToUpper(rtype)
if rtype == util.JSON {
// If the route type is JSON, then return the handler response as JSON
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(handlerResponse)
Expand All @@ -222,6 +223,15 @@ func (a *App) createHandlerFunc(html, block string, handler starlark.Callable, r
return
}
return
} else if rtype == util.TEXT {
// If the route type is TEXT, then return the handler response as text
w.Header().Set("Content-Type", "text/plain")
_, err := fmt.Fprint(w, handlerResponse)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
return
}

requestData.Data = handlerResponse
Expand Down Expand Up @@ -301,9 +311,9 @@ func (a *App) handleResponse(retStruct *starlarkstruct.Struct, r *http.Request,
// Default to the type set at the route level
responseRtype = rtype
}

if templateBlock == "" && responseRtype != "json" {
return false, fmt.Errorf("block not defined in response and type is not json")
responseRtype = strings.ToUpper(responseRtype)
if templateBlock == "" && responseRtype != util.JSON && responseRtype != util.TEXT {
return false, fmt.Errorf("block not defined in response and type is not json/text")
}

code, err := util.GetIntAttr(retStruct, "code")
Expand Down Expand Up @@ -341,7 +351,7 @@ func (a *App) handleResponse(retStruct *starlarkstruct.Struct, r *http.Request,
return true, nil
}

if strings.ToLower(responseRtype) == "json" {
if strings.ToUpper(responseRtype) == util.JSON {
if deferredCleanup != nil && deferredCleanup() != nil {
return true, nil
}
Expand All @@ -353,6 +363,18 @@ func (a *App) handleResponse(retStruct *starlarkstruct.Struct, r *http.Request,
return true, nil
}
return true, nil
} else if strings.ToUpper(responseRtype) == util.TEXT {
if deferredCleanup != nil && deferredCleanup() != nil {
return true, nil
}
// If the route type is TEXT, then return the handler response as plain text
w.Header().Set("Content-Type", "text/plain")
_, err := fmt.Fprint(w, templateValue)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return true, nil
}
return true, nil
}

requestData.Data = templateValue
Expand Down
5 changes: 3 additions & 2 deletions internal/app/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io"
"net/http"
"path"
"strings"

"github.com/claceio/clace/internal/app/dev"
"github.com/claceio/clace/internal/app/util"
Expand Down Expand Up @@ -307,7 +308,7 @@ func (a *App) addPageRoute(count int, router *chi.Mux, pageVal starlark.Value, d
return err
}

if rtypeStr == "" || rtypeStr == "html" {
if rtypeStr == "" || strings.ToUpper(rtypeStr) == util.HTML {
a.usesHtmlTemplate = true
}

Expand Down Expand Up @@ -376,7 +377,7 @@ func (a *App) handleFragments(router *chi.Mux, pagePath string, pageCount int, h
return err
}

if rtypeStr == "" || rtypeStr == "html" {
if rtypeStr == "" || strings.ToUpper(rtypeStr) == util.HTML {
a.usesHtmlTemplate = true
}

Expand Down
28 changes: 26 additions & 2 deletions internal/app/tests/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func TestAppLoadNoHtmlCustomLayout(t *testing.T) {
logger := testutil.TestLogger()
fileData := map[string]string{
"app.star": `
app = ace.app("testApp", custom_layout=True, pages = [ace.page("/", type="json")])
app = ace.app("testApp", custom_layout=True, pages = [ace.page("/", type=ace.JSON)])
def handler(req):
return {"key": "myvalue"}
Expand All @@ -142,6 +142,30 @@ def handler(req):
testutil.AssertStringContains(t, response.Body.String(), `{"key":"myvalue"}`)
}

func TestAppLoadPlain(t *testing.T) {
logger := testutil.TestLogger()
fileData := map[string]string{
"app.star": `
app = ace.app("testApp", pages = [ace.page("/", type=ace.TEXT)])
def handler(req):
return "abc"
`,
}
a, _, err := CreateDevModeTestApp(logger, fileData)
if err != nil {
t.Fatalf("Error %s", err)
}

request := httptest.NewRequest("GET", "/test", nil)
response := httptest.NewRecorder()

a.ServeHTTP(response, request)

testutil.AssertEqualsInt(t, "code", 200, response.Code)
testutil.AssertEqualsString(t, "body", "abc", response.Body.String())
}

func TestAppLoadWithLockfile(t *testing.T) {
logger := testutil.TestLogger()
fileData := map[string]string{
Expand Down Expand Up @@ -404,7 +428,7 @@ func TestPost(t *testing.T) {
logger := testutil.TestLogger()
fileData := map[string]string{
"app.star": `
app = ace.app("testApp", custom_layout=True, pages = [ace.page("/", method="POST")])
app = ace.app("testApp", custom_layout=True, pages = [ace.page("/", method=ace.POST)])
def handler(req):
return ace.redirect("/new_url", code=302)`,
}
Expand Down
28 changes: 26 additions & 2 deletions internal/app/tests/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,30 @@ def handler(req):
testutil.AssertEqualsInt(t, "b", int(ret["b"].(float64)), 1)
}

func TestRTypeText(t *testing.T) {
logger := testutil.TestLogger()
fileData := map[string]string{
"app.star": `
app = ace.app("testApp", pages = [ace.page("/", type=ace.JSON)])
def handler(req):
return ace.response(100, type=ace.TEXT)`,
}
a, _, err := CreateTestApp(logger, fileData)
if err != nil {
t.Fatalf("Error %s", err)
}

request := httptest.NewRequest("GET", "/test", nil)
response := httptest.NewRecorder()
a.ServeHTTP(response, request)

testutil.AssertEqualsInt(t, "code", 200, response.Code)
testutil.AssertStringContains(t, response.Header().Get("Content-Type"), "text/plain")
testutil.AssertEqualsString(t, "body", "100", response.Body.String())

}

func TestRTypeResponseInherit(t *testing.T) {
logger := testutil.TestLogger()
fileData := map[string]string{
Expand Down Expand Up @@ -187,7 +211,7 @@ def handler(req):
a.ServeHTTP(response, request)

testutil.AssertEqualsInt(t, "code", 500, response.Code)
testutil.AssertEqualsString(t, "error", "Error handling response: block not defined in response and type is not json\n", response.Body.String())
testutil.AssertEqualsString(t, "error", "Error handling response: block not defined in response and type is not json/text\n", response.Body.String())
}

func TestRTypeInvalidType(t *testing.T) {
Expand All @@ -200,5 +224,5 @@ def handler(req):
return ace.response({"a": "aval", "b": 1})`,
}
_, _, err := CreateDevModeTestApp(logger, fileData)
testutil.AssertErrorContains(t, err, "invalid type specified : abc")
testutil.AssertErrorContains(t, err, "invalid type specified : ABC")
}
31 changes: 25 additions & 6 deletions internal/app/util/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ const (
DEFAULT_REDIRECT_CODE = 303
)

const (
// Constants included in the ace builtin module
GET = "GET"
POST = "POST"
PUT = "PUT"
DELETE = "DELETE"
HTML = "HTML"
JSON = "JSON"
TEXT = "TEXT"
)

var (
once sync.Once
builtin starlark.StringDict
Expand Down Expand Up @@ -92,8 +103,8 @@ func createPageBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tu
fragments = starlark.NewList([]starlark.Value{})
}

rtypeStr := strings.ToLower(rtype.GoString())
if rtypeStr != "" && rtypeStr != "html" && rtypeStr != "json" {
rtypeStr := strings.ToUpper(rtype.GoString())
if rtypeStr != "" && rtypeStr != HTML && rtypeStr != JSON && rtypeStr != TEXT {
return nil, fmt.Errorf("invalid type specified : %s", rtypeStr)
}

Expand Down Expand Up @@ -124,8 +135,8 @@ func createFragmentBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlar
method = "GET"
}

rtypeStr := strings.ToLower(rtype.GoString())
if rtypeStr != "" && rtypeStr != "html" && rtypeStr != "json" {
rtypeStr := strings.ToUpper(rtype.GoString())
if rtypeStr != "" && rtypeStr != HTML && rtypeStr != JSON && rtypeStr != TEXT {
return nil, fmt.Errorf("invalid type specified : %s", rtypeStr)
}

Expand Down Expand Up @@ -195,8 +206,8 @@ func createResponseBuiltin(_ *starlark.Thread, _ *starlark.Builtin, args starlar
code = starlark.MakeInt(http.StatusOK)
}

rtypeStr := strings.ToLower(rtype.GoString())
if rtypeStr != "" && rtypeStr != "html" && rtypeStr != "json" {
rtypeStr := strings.ToUpper(rtype.GoString())
if rtypeStr != "" && rtypeStr != HTML && rtypeStr != JSON && rtypeStr != TEXT {
return nil, fmt.Errorf("invalid type specified : %s", rtypeStr)
}

Expand Down Expand Up @@ -276,6 +287,14 @@ func CreateBuiltin() starlark.StringDict {
STYLE: starlark.NewBuiltin(STYLE, createStyleBuiltin),
RESPONSE: starlark.NewBuiltin(RESPONSE, createResponseBuiltin),
LIBRARY: starlark.NewBuiltin(LIBRARY, createLibraryBuiltin),

GET: starlark.String(GET),
POST: starlark.String(POST),
PUT: starlark.String(PUT),
DELETE: starlark.String(DELETE),
HTML: starlark.String(HTML),
JSON: starlark.String(JSON),
TEXT: starlark.String(TEXT),
},
},
}
Expand Down

0 comments on commit e5cd6c2

Please sign in to comment.