Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdata committed Jun 28, 2024
1 parent fe7358e commit c63a980
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 7 deletions.
5 changes: 4 additions & 1 deletion jaws.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,10 @@ function jawsVar(name, data, operation) {
data = obj[lastkey](data);
break;
case 'Set':
return (obj[lastkey] = data);
if (typeof obj[lastkey] !== 'function') {
obj[lastkey] = data;

Check warning

Code scanning / CodeQL

Prototype-polluting function Medium

The property chain
here
is recursively assigned to
obj
without guarding against prototype pollution.
}
return data;
default:
throw "jaws: unknown operation: " + operation;
}
Expand Down
43 changes: 43 additions & 0 deletions jsany.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package jaws

import (
"encoding/json"
"io"

"github.com/linkdata/jaws/what"
)

type JsAny struct {
JsVariable
AnySetter
}

func (ui *JsAny) JawsRender(e *Element, w io.Writer, params []any) error {
return ui.render(ui.AnySetter, ui.JawsGetAny(e), e, w, params)
}

func (ui *JsAny) JawsUpdate(e *Element) {
_ = e.JsSet(ui.JawsGetAny(e))
}

func (ui *JsAny) JawsEvent(e *Element, wht what.What, val string) (err error) {
err = ErrEventUnhandled
if wht == what.Set {
var v any
if err = json.Unmarshal([]byte(val), &v); err == nil {
_, err = e.maybeDirty(ui.Tag, ui.JawsSetAny(e, v))
}
}
return
}

func NewJsAny(g AnySetter, name string) *JsAny {
return &JsAny{
JsVariable: JsVariable{JsName{Name: name}},
AnySetter: g,
}
}

func (rq RequestWriter) JsAny(value any, name string, params ...any) error {
return rq.UI(NewJsAny(makeAnySetter(value), name), params...)
}
73 changes: 73 additions & 0 deletions jsany_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package jaws

import (
"strconv"
"testing"

"github.com/linkdata/jaws/what"
)

func TestJsAny_JawsRender(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
rq := newTestRequest()
defer rq.Close()

var val any
ts := newTestSetter(val)
th.NoErr(rq.JsAny(ts, "varname"))
wantHtml := "<div id=\"Jid.1\" data-jawsname=\"varname\" hidden></div>"
if gotHtml := rq.BodyString(); gotHtml != wantHtml {
t.Errorf("Request.JsAny() = %q, want %q", gotHtml, wantHtml)
}

ts.Set(1.3)
rq.Dirty(ts)

select {
case <-th.C:
th.Timeout()
case msg := <-rq.outCh:
s := msg.Format()
if s != "Set\tJid.1\t1.3\n" {
t.Error(strconv.Quote(s))
}
}
}

func TestJsAny_JawsEvent(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
rq := newTestRequest()
defer rq.Close()

msgCh := make(chan string, 1)
defer close(msgCh)

val := float64(1.2)
ts := newTestSetter(any(val))
ui := NewJsAny(ts, "varname")
th.NoErr(rq.UI(ui))

th.Equal(ui.JawsGetTag(rq.Request), ts)

select {
case <-th.C:
th.Timeout()
case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}:
}

select {
case <-th.C:
th.Timeout()
case <-ts.setCalled:
}

th.Equal(ts.Get(), 1.3)

select {
case <-th.C:
th.Timeout()
case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}:
}
}
45 changes: 45 additions & 0 deletions jsfunction.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
package jaws

import (
"encoding/json"
"io"

"github.com/linkdata/jaws/what"
)

type JsFunction struct {
JsName
Param AnySetter
Result AnySetter
ResultTag any
}

func (ui *JsFunction) JawsRender(e *Element, w io.Writer, params []any) error {
ui.ResultTag = e.ApplyGetter(ui.Result)
return ui.render(ui.Param, nil, e, w, params)
}

func (ui *JsFunction) JawsUpdate(e *Element) {
_ = e.JsCall(ui.Param.JawsGetAny(e))
}

func (ui *JsFunction) JawsEvent(e *Element, wht what.What, val string) (err error) {
err = ErrEventUnhandled
if wht == what.Set && ui.ResultTag != nil {
var v any
if err = json.Unmarshal([]byte(val), &v); err == nil {
_, err = e.maybeDirty(ui.ResultTag, ui.Result.JawsSetAny(e, v))
}
}
return
}

func NewJsFunction(param, result AnySetter, name string) *JsFunction {
if param == nil {
panic("NewJsFunction param is nil")
}
return &JsFunction{
JsName: JsName{Name: name},
Param: param,
Result: result,
}
}

func (rq RequestWriter) JsFunction(param, result any, name string, params ...any) error {
return rq.UI(NewJsFunction(makeAnySetter(param), makeAnySetter(result), name), params...)
}
85 changes: 85 additions & 0 deletions jsfunction_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package jaws

import (
"strconv"
"testing"

"github.com/linkdata/jaws/what"
)

func TestJsFunction_JawsRender(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
rq := newTestRequest()
defer rq.Close()

param := any("meh")
tsparam := newTestSetter(param)
result := any("foo")
tsresult := newTestSetter(result)
ui := NewJsFunction(tsparam, tsresult, "fnname")
th.NoErr(rq.UI(ui))
wantHtml := "<div id=\"Jid.1\" data-jawsname=\"fnname\" hidden></div>"
if gotHtml := rq.BodyString(); gotHtml != wantHtml {
t.Errorf("Request.JsFunction() = %q, want %q", gotHtml, wantHtml)
}

tsparam.Set(1.3)
rq.Dirty(tsparam)

select {
case <-th.C:
th.Timeout()
case msg := <-rq.outCh:
s := msg.Format()
if s != "Call\tJid.1\t1.3\n" {
t.Error(strconv.Quote(s))
}
}
}

func TestJsFunction_PanicsIfParamNil(t *testing.T) {
is := newTestHelper(t)
rq := newTestRequest()
defer rq.Close()
defer func() {
if x := recover(); x == nil {
is.Fail()
}
}()
NewJsFunction(nil, nil, "fnname")
is.Fail()
}

func TestJsFunction_JawsEvent(t *testing.T) {
th := newTestHelper(t)
nextJid = 0
rq := newTestRequest()
defer rq.Close()

param := any("foo")
tsparam := newTestSetter(param)
result := any(float64(1.2))
tsresult := newTestSetter(result)
th.NoErr(rq.JsFunction(tsparam, tsresult, "fnname"))

select {
case <-th.C:
th.Timeout()
case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}:
}

select {
case <-th.C:
th.Timeout()
case <-tsresult.setCalled:
}

th.Equal(tsresult.Get(), 1.3)

select {
case <-th.C:
th.Timeout()
case rq.inCh <- wsMsg{Jid: 1, What: what.Set, Data: "1.3"}:
}
}
3 changes: 0 additions & 3 deletions jsstring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ func TestJsString_JawsEvent(t *testing.T) {
rq := newTestRequest()
defer rq.Close()

msgCh := make(chan string, 1)
defer close(msgCh)

val := "text'1"
ts := newTestSetter(val)
th.NoErr(rq.JsString(ts, "varname"))
Expand Down
8 changes: 6 additions & 2 deletions jsvariable.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ type JsVariable struct {

func (ui *JsVariable) render(getter any, val any, e *Element, w io.Writer, params []any) (err error) {
var data []byte
if data, err = json.Marshal(val); err == nil {
data = bytes.ReplaceAll(data, []byte(`'`), []byte(`\u0027`))
if val != nil {
if data, err = json.Marshal(val); err == nil {
data = bytes.ReplaceAll(data, []byte(`'`), []byte(`\u0027`))
}
}
if err == nil {
err = ui.JsName.render(getter, data, e, w, params)
}
return
Expand Down
2 changes: 1 addition & 1 deletion wsmsg.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func wsParse(txt []byte) (wsMsg, bool) {
if wht := what.Parse(string(txt[0:nl1])); wht.IsValid() {
if id := jid.ParseString(string(txt[nl1+1 : nl2])); id.IsValid() {
data := string(txt[nl2+1 : len(txt)-1])
if txt[nl2+1] == '"' {
if wht != what.Set && txt[nl2+1] == '"' {
var err error
if data, err = strconv.Unquote(data); err != nil {
return wsMsg{}, false
Expand Down

0 comments on commit c63a980

Please sign in to comment.