updated cobra.Command constructor and update structConstructor to use goja.Object.Set

This commit is contained in:
Gani Georgiev 2023-06-21 20:36:57 +03:00
parent fc311a8d28
commit 21607f0f66
4 changed files with 9713 additions and 6733 deletions

View File

@ -14,7 +14,7 @@ const heading = `
// baseBinds
// -------------------------------------------------------------------
declare var $app: core.App
declare var $app: pocketbase.PocketBase
interface Record extends models.Record{} // merge
declare class Record implements models.Record {
@ -41,11 +41,16 @@ declare class SchemaField implements schema.SchemaField {
constructor(data?: Partial<schema.SchemaField>)
}
interface Mail extends mailer.Message{} // merge
interface MailerMessage extends mailer.Message{} // merge
declare class Mail implements mailer.Message {
constructor(message?: Partial<mailer.Message>)
}
interface Command extends cobra.Command{} // merge
declare class Mail implements cobra.Command {
constructor(cmd?: Partial<cobra.Command>)
}
interface ValidationError extends ozzo_validation.Error{} // merge
declare class ValidationError implements ozzo_validation.Error {
constructor(code?: number, message?: string)
@ -270,7 +275,7 @@ func main() {
"github.com/pocketbase/pocketbase/tokens": {"*"},
"github.com/pocketbase/pocketbase/apis": {"*"},
"github.com/pocketbase/pocketbase/forms": {"*"},
"github.com/pocketbase/pocketbase/core": {"*"},
"github.com/pocketbase/pocketbase": {"*"},
},
FieldNameFormatter: func(s string) string {
return mapper.FieldName(nil, reflect.StructField{Name: s})

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@ package jsvm
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"reflect"
@ -38,6 +39,7 @@ import (
"github.com/pocketbase/pocketbase/tools/mailer"
"github.com/pocketbase/pocketbase/tools/security"
"github.com/pocketbase/pocketbase/tools/types"
"github.com/spf13/cobra"
)
func baseBinds(vm *goja.Runtime) {
@ -66,19 +68,6 @@ func baseBinds(vm *goja.Runtime) {
}
`)
vm.Set("unmarshal", func(src map[string]any, dest any) (any, error) {
raw, err := json.Marshal(src)
if err != nil {
return nil, err
}
if err := json.Unmarshal(raw, &dest); err != nil {
return nil, err
}
return dest, nil
})
vm.Set("DynamicModel", func(call goja.ConstructorCall) *goja.Object {
shape, ok := call.Argument(0).Export().(map[string]any)
if !ok || len(shape) == 0 {
@ -113,9 +102,7 @@ func baseBinds(vm *goja.Runtime) {
instance = models.NewRecord(collection)
data, ok := call.Argument(1).Export().(map[string]any)
if ok {
if raw, err := json.Marshal(data); err == nil {
json.Unmarshal(raw, instance)
}
instance.Load(data)
}
} else {
instance = &models.Record{}
@ -129,26 +116,31 @@ func baseBinds(vm *goja.Runtime) {
vm.Set("Collection", func(call goja.ConstructorCall) *goja.Object {
instance := &models.Collection{}
return structConstructor(vm, call, instance)
return structConstructorUnmarshal(vm, call, instance)
})
vm.Set("Admin", func(call goja.ConstructorCall) *goja.Object {
instance := &models.Admin{}
return structConstructor(vm, call, instance)
return structConstructorUnmarshal(vm, call, instance)
})
vm.Set("Schema", func(call goja.ConstructorCall) *goja.Object {
instance := &schema.Schema{}
return structConstructor(vm, call, instance)
return structConstructorUnmarshal(vm, call, instance)
})
vm.Set("SchemaField", func(call goja.ConstructorCall) *goja.Object {
instance := &schema.SchemaField{}
return structConstructorUnmarshal(vm, call, instance)
})
vm.Set("MailerMessage", func(call goja.ConstructorCall) *goja.Object {
instance := &mailer.Message{}
return structConstructor(vm, call, instance)
})
vm.Set("Mail", func(call goja.ConstructorCall) *goja.Object {
instance := &mailer.Message{}
vm.Set("Command", func(call goja.ConstructorCall) *goja.Object {
instance := &cobra.Command{}
return structConstructor(vm, call, instance)
})
@ -307,8 +299,8 @@ func apisBinds(vm *goja.Runtime) {
obj.Set("unauthorizedError", apis.NewUnauthorizedError)
vm.Set("Route", func(call goja.ConstructorCall) *goja.Object {
instance := echo.Route{}
return structConstructor(vm, call, &instance)
instance := &echo.Route{}
return structConstructor(vm, call, instance)
})
}
@ -339,7 +331,25 @@ func registerFactoryAsConstructor(vm *goja.Runtime, constructorName string, fact
}
// structConstructor wraps the provided struct with a native JS constructor.
//
// If the constructor argument is a map, each entry of the map will be loaded into the wrapped goja.Object.
func structConstructor(vm *goja.Runtime, call goja.ConstructorCall, instance any) *goja.Object {
data, _ := call.Argument(0).Export().(map[string]any)
instanceValue := vm.ToValue(instance).(*goja.Object)
for k, v := range data {
instanceValue.Set(k, v)
}
instanceValue.SetPrototype(call.This.Prototype())
return instanceValue
}
// structConstructorUnmarshal wraps the provided struct with a native JS constructor.
//
// The constructor first argument will be loaded via json.Unmarshal into the instance.
func structConstructorUnmarshal(vm *goja.Runtime, call goja.ConstructorCall, instance any) *goja.Object {
if data := call.Argument(0).Export(); data != nil {
if raw, err := json.Marshal(data); err == nil {
json.Unmarshal(raw, instance)
@ -460,3 +470,42 @@ func newDynamicModel(shape map[string]any) any {
return elem.Addr().Interface()
}
func loadMapFields(data any, instance any) error {
if reflect.TypeOf(data).Kind() != reflect.Map {
return errors.New("data must be map")
}
if reflect.TypeOf(instance).Kind() != reflect.Pointer {
return errors.New("instance must be pointer")
}
iv := reflect.ValueOf(instance).Elem()
if iv.Kind() != reflect.Struct {
return errors.New("value must be a pointer to a struct/interface")
}
dv := reflect.ValueOf(data)
for _, k := range dv.MapKeys() {
name := strings.Title(k.String()) // @todo reverse mapping
field := iv.FieldByName(name)
if !field.CanSet() {
continue
}
v := dv.MapIndex(k)
if !v.CanInterface() {
continue
}
// if v.Type().Kind() == reflect.Func {
// }
// field.Set(reflect.ValueOf(v.Interface()))
}
return nil
}

View File

@ -27,25 +27,6 @@ func TestBaseBindsCount(t *testing.T) {
testBindsCount(vm, "this", 14, t)
}
func TestBaseBindsUnmarshal(t *testing.T) {
vm := goja.New()
baseBinds(vm)
v, err := vm.RunString(`unmarshal({ name: "test" }, new Collection())`)
if err != nil {
t.Fatal(err)
}
m, ok := v.Export().(*models.Collection)
if !ok {
t.Fatalf("Expected models.Collection, got %v", m)
}
if m.Name != "test" {
t.Fatalf("Expected collection with name %q, got %q", "test", m.Name)
}
}
func TestBaseBindsRecord(t *testing.T) {
app, _ := tests.NewTestApp()
defer app.Cleanup()
@ -168,11 +149,11 @@ func TestBaseBindsSchemaField(t *testing.T) {
}
}
func TestBaseBindsMail(t *testing.T) {
func TestBaseBindsMailerMessage(t *testing.T) {
vm := goja.New()
baseBinds(vm)
v, err := vm.RunString(`new Mail({
v, err := vm.RunString(`new MailerMessage({
from: {name: "test_from", address: "test_from@example.com"},
to: [
{name: "test_to1", address: "test_to1@example.com"},
@ -212,6 +193,35 @@ func TestBaseBindsMail(t *testing.T) {
}
}
func TestBaseBindsCommand(t *testing.T) {
vm := goja.New()
baseBinds(vm)
_, err := vm.RunString(`
let runCalls = 0;
let cmd = new Command({
use: "test",
run: (c, args) => {
runCalls++;
}
});
cmd.run(null, []);
if (cmd.use != "test") {
throw new Error('Expected cmd.use "test", got: ' + cmd.use);
}
if (runCalls != 1) {
throw new Error('Expected runCalls 1, got: ' + runCalls);
}
`)
if err != nil {
t.Fatal(err)
}
}
func TestBaseBindsValidationError(t *testing.T) {
vm := goja.New()
baseBinds(vm)