From 94b6a15f0c5a1f65c948295ec6ddcfd97a19514b Mon Sep 17 00:00:00 2001 From: Manuel BARRAUD Date: Thu, 31 Oct 2024 10:14:11 +0100 Subject: [PATCH] Feature/transformstream (#158) * improve xmlhttprequest , support transformstream * add flush func, correct unit test --- base/baseobject/baseobject.go | 9 ++ .../serviceworkerregistration.go | 2 - base/stream/errors.go | 12 +- base/stream/readablestream.go | 3 +- base/stream/readablestreamdefaultreader.go | 2 +- base/stream/transformstream.go | 115 +++++++++++++++++- .../transformstreamdefaultcontroller.go | 85 +++++++++++++ base/stream/writablestreamdefaultwriter.go | 1 - base/typedarray/int8array.go | 2 - base/typedarray/typedarray.go | 1 - base/xmlhttprequest/xmlhttprequest.go | 24 ++++ base/xmlhttprequest/xmlhttprequest_test.go | 108 ++++++++++++---- 12 files changed, 324 insertions(+), 40 deletions(-) create mode 100644 base/stream/transformstreamdefaultcontroller.go diff --git a/base/baseobject/baseobject.go b/base/baseobject/baseobject.go index 3a7a959..5537e64 100755 --- a/base/baseobject/baseobject.go +++ b/base/baseobject/baseobject.go @@ -255,6 +255,15 @@ func NewFromJSObject(obj js.Value) (BaseObject, error) { } +// NewEmptyObject empty object. +func NewEmptyObject() (BaseObject, error) { + var o BaseObject + obj := js.ValueOf(map[string]interface{}{}) + o.object = &obj + return o, nil + +} + // Empty check if the struct is an empty Struct or have a JS Value attached func (b BaseObject) Empty() bool { diff --git a/base/serviceworkerregistration/serviceworkerregistration.go b/base/serviceworkerregistration/serviceworkerregistration.go index a433fbf..689d53a 100644 --- a/base/serviceworkerregistration/serviceworkerregistration.go +++ b/base/serviceworkerregistration/serviceworkerregistration.go @@ -1,7 +1,6 @@ package serviceworkerregistration import ( - "fmt" "sync" "syscall/js" @@ -82,7 +81,6 @@ func (s ServiceWorkerRegistration) getserviceworkerAttribute(attribute string) ( return sw, baseobject.ErrUndefinedValue } else { if sw, ok = obj.(serviceworker.ServiceWorker); !ok { - fmt.Printf("-->%v\n", obj.(baseobject.BaseObject).ConstructName_()) err = serviceworker.ErrNotAServiceWorker } diff --git a/base/stream/errors.go b/base/stream/errors.go index 352e775..9166d83 100755 --- a/base/stream/errors.go +++ b/base/stream/errors.go @@ -3,8 +3,12 @@ package stream import "errors" var ( - ErrNotImplementedReadableStream = errors.New("Browser not implemented ReadableStream") - ErrNotImplementedWritableStream = errors.New("Browser not implemented WritableStream") - ErrNotAReadableStream = errors.New("Object is not a ReadableStream") - ErrNotAWritableStream = errors.New("Object is not a WritableStream") + ErrNotImplementedReadableStream = errors.New("Browser not implemented ReadableStream") + ErrNotImplementedWritableStream = errors.New("Browser not implemented WritableStream") + ErrNotImplementedTransformStream = errors.New("Browser not implemented TransformStream") + ErrNotAReadableStream = errors.New("Object is not a ReadableStream") + ErrNotAWritableStream = errors.New("Object is not a WritableStream") + ErrNotATransformStream = errors.New("Object is not a TransformStream") + ErrNotAReadableStreamDefaultReader = errors.New("Object is not a ReadableStreamDefaultReader") + ErrNotATransformStreamDefaultController = errors.New("Object is not a TransformStreamDefaultController") ) diff --git a/base/stream/readablestream.go b/base/stream/readablestream.go index b071507..6216acf 100755 --- a/base/stream/readablestream.go +++ b/base/stream/readablestream.go @@ -16,9 +16,10 @@ func init() { initinterface.RegisterInterface(GetRInterface) initinterface.RegisterInterface(GetWInterface) + initinterface.RegisterInterface(GetTInterface) initinterface.RegisterInterface(GetReadableStreamDefaultReaderInterface) initinterface.RegisterInterface(GetWritableStreamDefaultWriterInterface) - + initinterface.RegisterInterface(GetTransformStreamDefaultControllerInterface) } var singletonr sync.Once diff --git a/base/stream/readablestreamdefaultreader.go b/base/stream/readablestreamdefaultreader.go index 84526b5..f65be2c 100755 --- a/base/stream/readablestreamdefaultreader.go +++ b/base/stream/readablestreamdefaultreader.go @@ -52,7 +52,7 @@ func NewReadableStreamDefaultReaderFromJSObject(obj js.Value) (ReadableStreamDef } } - return r, ErrNotAReadableStream + return r, ErrNotAReadableStreamDefaultReader } func (r ReadableStreamDefaultReader) newRead(data []byte, dataHandle func([]byte, int)) *promise.Promise { diff --git a/base/stream/transformstream.go b/base/stream/transformstream.go index 3ca4d5c..55236e1 100644 --- a/base/stream/transformstream.go +++ b/base/stream/transformstream.go @@ -1,9 +1,122 @@ package stream -import "github.com/realPy/hogosuru/base/baseobject" +import ( + "sync" + "syscall/js" + + "github.com/realPy/hogosuru/base/baseobject" +) type TransformStream struct { baseobject.BaseObject + start, transform, flush js.Func +} + +var singletont sync.Once + +var transformstreaminterface js.Value + +// GetRInterface get the JS interface ReadableStream. +func GetTInterface() js.Value { + + singletont.Do(func() { + + var err error + if transformstreaminterface, err = baseobject.Get(js.Global(), "TransformStream"); err != nil { + transformstreaminterface = js.Undefined() + } + baseobject.Register(transformstreaminterface, func(v js.Value) (interface{}, error) { + return NewTransformStreamFromJSObject(v) + }) + }) + + return transformstreaminterface +} + +// NewReadableStream Create a new ReadableStream +func NewTransformStream( + start func(controller TransformStreamDefaultController), + transform func(chunk interface{}, controller TransformStreamDefaultController), + flush func(controller TransformStreamDefaultController), +) (TransformStream, error) { + var t TransformStream + var obj js.Value + var err error + if ri := GetTInterface(); !ri.IsUndefined() { + ob, _ := baseobject.NewEmptyObject() + t.start = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + + if c, err := baseobject.Discover(args[0]); err == nil { + if ctrl, ok := c.(TransformStreamDefaultController); ok && start != nil { + start(ctrl) + } + } + return nil + }) + t.transform = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + if c, err := baseobject.Discover(args[1]); err == nil { + if ctrl, ok := c.(TransformStreamDefaultController); ok && transform != nil { + chunk, _ := baseobject.Discover(args[0]) + transform(chunk, ctrl) + + } + } + + return nil + }) + + t.flush = js.FuncOf(func(this js.Value, args []js.Value) interface{} { + if c, err := baseobject.Discover(args[0]); err == nil { + if ctrl, ok := c.(TransformStreamDefaultController); ok && flush != nil { + flush(ctrl) + + } + } + return nil + }) + + ob.Set("start", t.start) + ob.Set("transform", t.transform) + ob.Set("flush", t.flush) + + if obj, err = baseobject.New(ri, ob.JSObject()); err == nil { + t.BaseObject = t.BaseObject.SetObject(obj) + + } + + } else { + err = ErrNotImplementedReadableStream + } + return t, err +} + +func NewTransformStreamFromJSObject(obj js.Value) (TransformStream, error) { + var t TransformStream + var err error + if tsi := GetTInterface(); !tsi.IsUndefined() { + if obj.IsUndefined() || obj.IsNull() { + err = baseobject.ErrUndefinedValue + } else { + + if obj.InstanceOf(tsi) { + t.BaseObject = t.SetObject(obj) + + } else { + err = ErrNotATransformStream + } + } + } else { + err = ErrNotImplementedTransformStream + } + + return t, err +} + +func (t *TransformStream) Release() { + + t.start.Release() + t.transform.Release() + t.flush.Release() } func TransfertToTransformStream(b baseobject.BaseObject) TransformStream { diff --git a/base/stream/transformstreamdefaultcontroller.go b/base/stream/transformstreamdefaultcontroller.go new file mode 100644 index 0000000..55825e2 --- /dev/null +++ b/base/stream/transformstreamdefaultcontroller.go @@ -0,0 +1,85 @@ +package stream + +import ( + "sync" + "syscall/js" + + "github.com/realPy/hogosuru/base/baseobject" +) + +var singletonTransformStreamDefaultController sync.Once + +var transformstreamdefaultcontrollerinterface js.Value + +// GetTransformStreamDefaultControllerInterface +func GetTransformStreamDefaultControllerInterface() js.Value { + singletonTransformStreamDefaultController.Do(func() { + + var err error + if transformstreamdefaultcontrollerinterface, err = baseobject.Get(js.Global(), "TransformStreamDefaultController"); err != nil { + transformstreamdefaultcontrollerinterface = js.Undefined() + } + + baseobject.Register(transformstreamdefaultcontrollerinterface, func(v js.Value) (interface{}, error) { + return NewTransformStreamDefaultControllerFromJSObject(v) + }) + }) + + return transformstreamdefaultcontrollerinterface +} + +type TransformStreamDefaultController struct { + baseobject.BaseObject +} + +type TransformStreamDefaultControllerFrom interface { + TransformStreamDefaultController_() TransformStreamDefaultController +} + +func (t TransformStreamDefaultController) TransformStreamDefaultController_() TransformStreamDefaultController { + return t +} + +func NewTransformStreamDefaultControllerFromJSObject(obj js.Value) (TransformStreamDefaultController, error) { + var t TransformStreamDefaultController + var err error + if wsi := GetTransformStreamDefaultControllerInterface(); !wsi.IsUndefined() { + if obj.IsUndefined() || obj.IsNull() { + err = baseobject.ErrUndefinedValue + } else { + + if obj.InstanceOf(wsi) { + t.BaseObject = t.SetObject(obj) + + } else { + err = ErrNotATransformStreamDefaultController + } + } + } else { + err = ErrNotImplementedTransformStream + } + + return t, err +} + +func (t TransformStreamDefaultController) Enqueue(chunk baseobject.BaseObject) error { + var err error + + if _, err = t.Call("enqueue", chunk.JSObject()); err != nil { + return err + + } + + return nil +} + +func (t TransformStreamDefaultController) Terminate() error { + var err error + + if _, err = t.Call("terminate"); err != nil { + return err + + } + + return nil +} diff --git a/base/stream/writablestreamdefaultwriter.go b/base/stream/writablestreamdefaultwriter.go index c08b3c3..8b60149 100644 --- a/base/stream/writablestreamdefaultwriter.go +++ b/base/stream/writablestreamdefaultwriter.go @@ -15,7 +15,6 @@ var writeablestreamdefaultinterface js.Value // GetWritableStreamDefaultWriterInterface func GetWritableStreamDefaultWriterInterface() js.Value { - singletonReadableStreamDefault.Do(func() { var err error diff --git a/base/typedarray/int8array.go b/base/typedarray/int8array.go index 1699157..59762be 100755 --- a/base/typedarray/int8array.go +++ b/base/typedarray/int8array.go @@ -60,12 +60,10 @@ func NewInt8Array(value interface{}) (Int8Array, error) { } func NewInt8ArrayFrom(iterable interface{}) (Int8Array, error) { - arr, err := newTypedArrayFrom(GetInt8ArrayInterface(), func(v js.Value) (interface{}, error) { return NewInt8FromJSObject(v) }, iterable) return arr.(Int8Array), err - } func NewInt8ArrayOf(values ...interface{}) (Int8Array, error) { diff --git a/base/typedarray/typedarray.go b/base/typedarray/typedarray.go index d50fe63..83172db 100755 --- a/base/typedarray/typedarray.go +++ b/base/typedarray/typedarray.go @@ -11,7 +11,6 @@ import ( ) func init() { - initinterface.RegisterInterface(GetFloat32ArrayInterface) initinterface.RegisterInterface(GetFloat64ArrayInterface) initinterface.RegisterInterface(GetInt8ArrayInterface) diff --git a/base/xmlhttprequest/xmlhttprequest.go b/base/xmlhttprequest/xmlhttprequest.go index a5147b6..3f2172a 100755 --- a/base/xmlhttprequest/xmlhttprequest.go +++ b/base/xmlhttprequest/xmlhttprequest.go @@ -195,6 +195,30 @@ func (x XMLHTTPRequest) GetResponseHeader(header string) (string, error) { return "", err } +// GetAllResponseHeader https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders +func (x XMLHTTPRequest) GetAllResponseHeader() (string, error) { + var responseHeader js.Value + var err error + if responseHeader, err = x.Call("getAllResponseHeaders"); err == nil { + + if responseHeader.Type() == js.TypeString { + return responseHeader.String(), nil + } else { + return "", baseobject.ErrObjectNotString + } + + } + return "", err +} + +// overrideMimeType https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType +func (x XMLHTTPRequest) OverrideMimeType(mimetype string) error { + var err error + _, err = x.Call("overrideMimeType", js.ValueOf(mimetype)) + + return err +} + // Response func (x XMLHTTPRequest) Response() (js.Value, error) { return x.Get("response") diff --git a/base/xmlhttprequest/xmlhttprequest_test.go b/base/xmlhttprequest/xmlhttprequest_test.go index 159da70..7082c12 100755 --- a/base/xmlhttprequest/xmlhttprequest_test.go +++ b/base/xmlhttprequest/xmlhttprequest_test.go @@ -42,52 +42,106 @@ func TestNewFromJSObject(t *testing.T) { func TestGetRequest(t *testing.T) { - var io chan bool = make(chan bool) + t.Run("get request with ResponseHeader", func(t *testing.T) { + var io chan bool = make(chan bool) - if xhr, err := New(); testingutils.AssertErr(t, err) { + if xhr, err := New(); testingutils.AssertErr(t, err) { - err := xhr.Open("GET", "http://localhost/get") - testingutils.AssertErr(t, err) + err := xhr.Open("GET", "http://localhost/get") + testingutils.AssertErr(t, err) - xhr.SetOnload(func(i interface{}) { + xhr.SetOnload(func(i interface{}) { - if status, err := xhr.Status(); testingutils.AssertErr(t, err) { - testingutils.AssertExpect(t, status, 200) - } + if status, err := xhr.Status(); testingutils.AssertErr(t, err) { + testingutils.AssertExpect(t, status, 200) + } - if header, err := xhr.GetResponseHeader("Content-Type"); testingutils.AssertErr(t, err) { + if header, err := xhr.GetResponseHeader("Content-Type"); testingutils.AssertErr(t, err) { - testingutils.AssertExpect(t, "application/json", header) + testingutils.AssertExpect(t, "application/json", header) - } - if text, err := xhr.ResponseText(); testingutils.AssertErr(t, err) { + } + if text, err := xhr.ResponseText(); testingutils.AssertErr(t, err) { - if j, err := json.Parse(text); testingutils.AssertErr(t, err) { - goValue := j.Map() + if j, err := json.Parse(text); testingutils.AssertErr(t, err) { + goValue := j.Map() - url := goValue.(map[string]interface{})["url"] + url := goValue.(map[string]interface{})["url"] + + if url != nil { + testingutils.AssertExpect(t, url, "http://localhost/get") + io <- true + } else { + t.Error("No url present") + } - if url != nil { - testingutils.AssertExpect(t, url, "http://localhost/get") - io <- true - } else { - t.Error("No url present") } } + }) + + xhr.Send() + + select { + case <-io: + case <-time.After(time.Duration(1000) * time.Millisecond): + t.Errorf("No message channel receive") } + } - }) + }) - xhr.Send() + t.Run("get request with GetAllResponseHeader", func(t *testing.T) { + var io chan bool = make(chan bool) - select { - case <-io: - case <-time.After(time.Duration(1000) * time.Millisecond): - t.Errorf("No message channel receive") + if xhr, err := New(); testingutils.AssertErr(t, err) { + + err := xhr.Open("GET", "http://localhost/get") + testingutils.AssertErr(t, err) + + xhr.SetOnload(func(i interface{}) { + + if status, err := xhr.Status(); testingutils.AssertErr(t, err) { + testingutils.AssertExpect(t, status, 200) + } + + if headers, err := xhr.GetAllResponseHeader(); testingutils.AssertErr(t, err) { + + testingutils.AssertStringContains(t, "content-type: application/json\r\n", headers) + testingutils.AssertStringContains(t, "content-length: ", headers) + + } + if text, err := xhr.ResponseText(); testingutils.AssertErr(t, err) { + + if j, err := json.Parse(text); testingutils.AssertErr(t, err) { + goValue := j.Map() + + url := goValue.(map[string]interface{})["url"] + + if url != nil { + testingutils.AssertExpect(t, url, "http://localhost/get") + io <- true + } else { + t.Error("No url present") + } + + } + + } + + }) + + xhr.Send() + + select { + case <-io: + case <-time.After(time.Duration(1000) * time.Millisecond): + t.Errorf("No message channel receive") + } } - } + }) + } func TestPostRequest(t *testing.T) {