Skip to content
This repository was archived by the owner on Sep 2, 2024. It is now read-only.

Commit 93a30f6

Browse files
committed
fixed bug with form submission and function exec runtime query function
1 parent c8ce469 commit 93a30f6

File tree

10 files changed

+154
-22
lines changed

10 files changed

+154
-22
lines changed

Makefile

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ deploy: build
1414
alltest:
1515
@JWT_SECRET=okdevmode go test --race --cover ./...
1616

17+
thistest:
18+
@JWT_SECRET=okdevmode go test -run "$2" --race --cover
19+
1720
test-pg:
1821
@JWT_SECRET=okdevmode go test --race --cover ./database/postgresql
1922

@@ -29,12 +32,12 @@ docker: build
2932
pkg: build
3033
@rm -rf dist/*
3134
@echo "building linux binaries"
32-
@CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o dist/binary-for-linux-64-bit
33-
@CGO_ENABLED=0 GOARCH=386 GOOS=linux go build -o dist/binary-for-linux-32-bit
35+
@cd cmd && CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o ../dist/binary-for-linux-64-bit
36+
@cd cmd && CGO_ENABLED=0 GOARCH=386 GOOS=linux go build -o ../dist/binary-for-linux-32-bit
3437
@echo "building mac binaries"
35-
@CGO_ENABLED=0 GOARCH=amd64 GOOS=darwin go build -o dist/binary-for-mac-64-bit
38+
@cd cmd && CGO_ENABLED=0 GOARCH=amd64 GOOS=darwin go build -o ../dist/binary-for-mac-64-bit
3639
@echo "building windows binaries"
37-
@CGO_ENABLED=0 GOARCH=amd64 GOOS=windows go build -o dist/binary-for-windows-64-bit.exe
38-
@CGO_ENABLED=0 GOARCH=386 GOOS=windows go build -o dist/binary-for-windows-32-bit.exe
40+
@cd cmd && CGO_ENABLED=0 GOARCH=amd64 GOOS=windows go build -o ../dist/binary-for-windows-64-bit.exe
41+
@cd cmd && CGO_ENABLED=0 GOARCH=386 GOOS=windows go build -o ../dist/binary-for-windows-32-bit.exe
3942
@echo "compressing binaries"
4043
@gzip dist/*

database/postgresql/base.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func (j *JSONB) Scan(value interface{}) error {
4242
func (pg *PostgreSQL) CreateDocument(auth internal.Auth, dbName, col string, doc map[string]interface{}) (inserted map[string]interface{}, err error) {
4343
inserted = doc
4444

45+
cleancol := internal.CleanCollectionName(col)
46+
4547
//TODO: find a good way to prevent doing the create
4648
// table if not exists each time
4749

@@ -53,7 +55,9 @@ func (pg *PostgreSQL) CreateDocument(auth internal.Auth, dbName, col string, doc
5355
data jsonb NOT NULL,
5456
created timestamp NOT NULL
5557
);
56-
`, dbName, internal.CleanCollectionName(col), dbName, dbName)
58+
59+
CREATE INDEX IF NOT EXISTS %s_acctid_idx ON %s.%s (account_id);
60+
`, dbName, cleancol, dbName, dbName, cleancol, dbName, cleancol)
5761

5862
if _, err = pg.DB.Exec(qry); err != nil {
5963
return

database/postgresql/sb.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func (pg *PostgreSQL) createSystemTables(schema string) error {
8585
data JSONB NOT NULL,
8686
created timestamp NOT NULL
8787
);
88+
CREATE INDEX IF NOT EXISTS sb_forms_name_idx ON {schema}.sb_forms (name);
8889
8990
CREATE TABLE IF NOT EXISTS {schema}.sb_files (
9091
id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
@@ -94,6 +95,7 @@ func (pg *PostgreSQL) createSystemTables(schema string) error {
9495
size INTEGER NOT NULL,
9596
uploaded timestamp NOT NULL
9697
);
98+
CREATE INDEX IF NOT EXISTS sb_files_acctid_idx ON {schema}.sb_files (account_id);
9799
98100
CREATE TABLE IF NOT EXISTS {schema}.sb_functions (
99101
id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),
@@ -104,6 +106,7 @@ func (pg *PostgreSQL) createSystemTables(schema string) error {
104106
last_updated timestamp NOT NULL,
105107
last_run timestamp NOT NULL
106108
);
109+
CREATE INDEX IF NOT EXISTS sb_functions_trigger_topic_idx ON {schema}.sb_functions (trigger_topic);
107110
108111
CREATE TABLE IF NOT EXISTS {schema}.sb_function_logs (
109112
id uuid PRIMARY KEY DEFAULT uuid_generate_v4 (),

db_test.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,52 @@ import (
77
"io/ioutil"
88
"net/http"
99
"net/http/httptest"
10+
"net/url"
1011
"testing"
1112
"time"
1213

1314
"github.com/staticbackendhq/core/internal"
1415
"github.com/staticbackendhq/core/middleware"
1516
)
1617

17-
// dbReq post on behalf of adminToken by default (use params[0] true for root)
18+
// dbReq post on behalf of adminToken by default (use:
19+
// params[0] true for root)
20+
// params[1] true for Content-Type application/x-www-form-urlencoded
1821
func dbReq(t *testing.T, hf func(http.ResponseWriter, *http.Request), method, path string, v interface{}, params ...bool) *http.Response {
1922
if params == nil {
20-
params = make([]bool, 0)
23+
params = make([]bool, 2)
2124
}
2225

23-
if len(params) == 0 {
26+
for i := len(params); i < 2; i++ {
2427
params = append(params, false)
2528
}
2629

27-
b, err := json.Marshal(v)
28-
if err != nil {
29-
t.Fatal("error marshaling post data:", err)
30+
var payload []byte
31+
if params[1] {
32+
val, ok := v.(url.Values)
33+
if !ok {
34+
t.Fatal("expected v to be url.Value")
35+
}
36+
37+
payload = []byte(val.Encode())
38+
} else {
39+
b, err := json.Marshal(v)
40+
if err != nil {
41+
t.Fatal("error marshaling post data:", err)
42+
}
43+
44+
payload = b
3045
}
3146

32-
req := httptest.NewRequest(method, path, bytes.NewReader(b))
47+
req := httptest.NewRequest(method, path, bytes.NewReader(payload))
3348
w := httptest.NewRecorder()
3449

50+
if params[1] {
51+
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
52+
} else {
53+
req.Header.Add("Content-Type", "application/json")
54+
}
55+
3556
req.Header.Set("SB-PUBLIC-KEY", pubKey)
3657

3758
tok := adminToken

form.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package staticbackend
22

33
import (
44
"net/http"
5+
"strings"
56

67
"github.com/staticbackendhq/core/middleware"
78
)
@@ -20,9 +21,16 @@ func submitForm(w http.ResponseWriter, r *http.Request) {
2021

2122
doc := make(map[string]interface{})
2223

23-
if err := r.ParseMultipartForm(32 << 20); err != nil {
24+
//TODO: Why forms would need multiplart/form-data
25+
// There's no file upload available via form
26+
/*if err := r.ParseMultipartForm(32 << 20); err != nil {
2427
http.Error(w, err.Error(), http.StatusInternalServerError)
2528
return
29+
}*/
30+
31+
if err := r.ParseForm(); err != nil {
32+
http.Error(w, err.Error(), http.StatusBadRequest)
33+
return
2634
}
2735

2836
// if there's something in the _hp_ field, it's a bot
@@ -31,6 +39,10 @@ func submitForm(w http.ResponseWriter, r *http.Request) {
3139
return
3240
}
3341

42+
for k, v := range r.Form {
43+
doc[k] = strings.Join(v, ", ")
44+
}
45+
3446
if err := datastore.AddFormSubmission(conf.Name, form, doc); err != nil {
3547
http.Error(w, err.Error(), http.StatusInternalServerError)
3648
return

form_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package staticbackend
2+
3+
import (
4+
"net/url"
5+
"testing"
6+
)
7+
8+
func TestFormSubmission(t *testing.T) {
9+
val := url.Values{}
10+
val.Add("name", "unit test")
11+
val.Add("email", "[email protected]")
12+
13+
resp := dbReq(t, submitForm, "POST", "/postform/testform", val, false, true)
14+
defer resp.Body.Close()
15+
16+
if resp.StatusCode > 299 {
17+
t.Fatal(GetResponseBody(t, resp))
18+
}
19+
20+
resp2 := dbReq(t, listForm, "GET", "/form?name=testform", nil, true)
21+
defer resp2.Body.Close()
22+
23+
var results []map[string]interface{}
24+
if err := parseBody(resp2.Body, &results); err != nil {
25+
t.Fatal(err)
26+
} else if len(results) == 0 {
27+
t.Errorf("expected to get at least one form submission got 0")
28+
} else if results[0]["name"] != "unit test" {
29+
t.Log(results)
30+
t.Errorf("expected name to be unit test got %v", results[0]["name"])
31+
} else if results[0]["email"] != "[email protected]" {
32+
t.Log(results)
33+
t.Errorf("expected email to be [email protected] got %v", results[0]["email"])
34+
}
35+
}

function/runtime.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"log"
88
"net/http"
9+
"strings"
910
"time"
1011

1112
"github.com/staticbackendhq/core/internal"
@@ -76,7 +77,7 @@ func (env *ExecutionEnvironment) prepareArguments(vm *goja.Runtime, data interfa
7677
defer r.Body.Close()
7778

7879
// let's ready the HTTP body
79-
if r.Header.Get("Content-Type") == "application/json" {
80+
if strings.EqualFold(r.Header.Get("Content-Type"), "application/json") {
8081
var v interface{}
8182
if err := json.NewDecoder(r.Body).Decode(&v); err != nil {
8283
return nil, err
@@ -87,7 +88,12 @@ func (env *ExecutionEnvironment) prepareArguments(vm *goja.Runtime, data interfa
8788
if err := r.ParseForm(); err != nil {
8889
return nil, err
8990
}
90-
args = append(args, vm.ToValue(r.Form))
91+
92+
val := make(map[string]interface{})
93+
for k, v := range r.Form {
94+
val[k] = strings.Join(v, ", ")
95+
}
96+
args = append(args, vm.ToValue(val))
9197
}
9298

9399
args = append(args, vm.ToValue(r.URL.Query()))
@@ -224,6 +230,12 @@ func (env *ExecutionEnvironment) addDatabaseFunctions(vm *goja.Runtime) {
224230
}
225231
}
226232

233+
// apply default page and limit
234+
if params.Size == 0 {
235+
params.Size = 25
236+
params.Page = 1
237+
}
238+
227239
result, err := env.DataStore.QueryDocuments(env.Auth, env.BaseName, col, filter, params)
228240
if err != nil {
229241
return vm.ToValue(Result{Content: fmt.Sprintf("error executing query: %v", err)})

functions.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,16 @@ func (f *functions) exec(w http.ResponseWriter, r *http.Request) {
8282
return
8383
}
8484

85-
var data internal.ExecData
85+
//TODONOW: this is not needed as only the fn name is required here
86+
/*var data internal.ExecData
8687
if err := parseBody(r.Body, &data); err != nil {
8788
http.Error(w, err.Error(), http.StatusBadRequest)
8889
return
89-
}
90+
}*/
91+
92+
functionName := getURLPart(r.URL.Path, 3)
9093

91-
fn, err := datastore.GetFunctionForExecution(conf.Name, data.FunctionName)
94+
fn, err := datastore.GetFunctionForExecution(conf.Name, functionName)
9295
if err != nil {
9396
http.Error(w, err.Error(), http.StatusInternalServerError)
9497
return

functions_test.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@ package staticbackend
33
import (
44
"io"
55
"net/http"
6+
"net/url"
7+
"strings"
68
"testing"
79

810
"github.com/staticbackendhq/core/internal"
911
)
1012

1113
func TestFunctionsExecuteDBOperations(t *testing.T) {
1214
code := `
13-
log("works here");
1415
function handle(body) {
16+
log(body);
1517
var o = {
18+
from: body.from,
1619
desc: "yep",
1720
done: false,
1821
subobj: {
@@ -31,6 +34,10 @@ func TestFunctionsExecuteDBOperations(t *testing.T) {
3134
log("ERROR: getting doc by id");
3235
log(getRes.content);
3336
return;
37+
} else if (getRes.content.from != "val from unit test") {
38+
log("ERROR: asserting data from request body");
39+
log(getRes.content);
40+
return;
3441
}
3542
3643
var updata = getRes.content;
@@ -52,6 +59,7 @@ func TestFunctionsExecuteDBOperations(t *testing.T) {
5259
if (qres.content.results.length != 1) {
5360
log("ERROR");
5461
log("expected results to have 1 doc, got: " + qres.content.results.length);
62+
log(qres);
5563
return;
5664
}
5765
@@ -81,7 +89,10 @@ func TestFunctionsExecuteDBOperations(t *testing.T) {
8189
t.Errorf("add: expected status 200 got %s", addResp.Status)
8290
}
8391

84-
execResp := dbReq(t, funexec.exec, "POST", "/", data, true)
92+
val := url.Values{}
93+
val.Add("from", "val from unit test")
94+
95+
execResp := dbReq(t, funexec.exec, "POST", "/fn/exec/unittest", val, false, true)
8596
if execResp.StatusCode != http.StatusOK {
8697
b, err := io.ReadAll(execResp.Body)
8798
if err != nil {
@@ -92,4 +103,32 @@ func TestFunctionsExecuteDBOperations(t *testing.T) {
92103
t.Log(string(b))
93104
t.Errorf("expected status 200 got %s", execResp.Status)
94105
}
106+
107+
infoResp := dbReq(t, funexec.info, "GET", "/fn/info/unittest", nil, true)
108+
109+
var checkFn internal.ExecData
110+
if err := parseBody(infoResp.Body, &checkFn); err != nil {
111+
t.Fatal(err)
112+
}
113+
defer infoResp.Body.Close()
114+
115+
var errorLines []string
116+
foundError := false
117+
for _, h := range checkFn.History {
118+
for _, line := range h.Output {
119+
if strings.Index(line, "ERROR") > -1 {
120+
errorLines = h.Output
121+
foundError = true
122+
break
123+
}
124+
}
125+
126+
if foundError {
127+
break
128+
}
129+
}
130+
131+
if foundError {
132+
t.Errorf("found error in function exec log: %v", errorLines)
133+
}
95134
}

server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func Start(dbHost, port string) {
192192
http.Handle("/fn/delete/", middleware.Chain(http.HandlerFunc(f.del), stdRoot...))
193193
http.Handle("/fn/del/", middleware.Chain(http.HandlerFunc(f.del), stdRoot...))
194194
http.Handle("/fn/info/", middleware.Chain(http.HandlerFunc(f.info), stdRoot...))
195-
http.Handle("/fn/exec", middleware.Chain(http.HandlerFunc(f.exec), stdAuth...))
195+
http.Handle("/fn/exec/", middleware.Chain(http.HandlerFunc(f.exec), stdAuth...))
196196
http.Handle("/fn", middleware.Chain(http.HandlerFunc(f.list), stdRoot...))
197197

198198
// ui routes

0 commit comments

Comments
 (0)