0x4a52466c696e74 3 months ago
parent
commit
854757e309
10 changed files with 223 additions and 65 deletions
  1. 1 1
      application/z_test.go
  2. 4 6
      example/user.go
  3. 1 1
      example_chat/users/register.go
  4. 113 40
      fielder.go
  5. 1 1
      go.mod
  6. 2 0
      go.sum
  7. 1 5
      request.go
  8. 4 0
      rest_http/request_in.go
  9. 4 0
      rest_websocket/request.go
  10. 92 11
      z_test.go

+ 1 - 1
application/z_test.go

@@ -32,7 +32,7 @@ func (s *Hello) Execute(req rest.IRequestIn) rest.IRequestOut {
 	response := HelloResponse{
 		Message: "Hello, " + s.Name,
 	}
-	return rest.OutFields(req, &response, nil)
+	return rest.OutFields(req, &response, nil, nil)
 }
 
 type HelloResponse struct {

+ 4 - 6
example/user.go

@@ -94,10 +94,8 @@ func (s *ExampleRequestRegister) Execute(req rest.IRequestIn) rest.IRequestOut {
 	// сохраняем пользлвателя в хранилище
 	App.users.Store(userID, user)
 	//fields, err := rest.Fields(user, files, req.RData().Slice("fields", nil)...)
-	fields, fErr := req.Fields(user, files)
-	if err != nil {
-		return req.OutError(fErr)
-	}
+
+	fields := rest.OutFields(req, user, files, nil)
 
 	// возвращаем успешный ответ
 	return req.OutSuccess(
@@ -131,8 +129,8 @@ func (s *ExampleRequestUserInfo) Validate(req rest.IRequestIn) rest.IRequestOut
 func (s *ExampleRequestUserInfo) Execute(req rest.IRequestIn) rest.IRequestOut {
 	files := make(map[string]rest.IReadCloserLen)
 	log.Println(s.user.Group)
-	fields := req.RData().Slice("fields", nil)
-	rFields, err := rest.Fields(s.user, files, fields...)
+	fields := rest.FieldListFromSlice(req.RData().Slice("fields", nil))
+	rFields, err := rest.Fields(s.user, files, fields)
 	log.Println(err)
 	if err != nil {
 		return req.OutError(err)

+ 1 - 1
example_chat/users/register.go

@@ -29,7 +29,7 @@ func (s *Register) Validate(req rest.IRequestIn) rest.IRequestOut {
 func (s *Register) Execute(req rest.IRequestIn) rest.IRequestOut {
 	core := req.RCore().(*chat.Chat)
 	user := core.Register(s.Name, s.Password)
-	fields, err := rest.Fields(user, nil)
+	fields, err := rest.Fields(user, nil, nil)
 	if err != nil {
 		return req.OutError(err)
 	}

+ 113 - 40
fielder.go

@@ -10,13 +10,81 @@ import (
 )
 
 type IRestFielder interface {
-	RestFields(fieldName string, names ...any) (any, IErrorArgs)
+	RestFields(fieldName string, names FieldList) (any, IErrorArgs)
+}
+
+func FieldFromName(name string) *Field {
+	return &Field{
+		Name: name,
+	}
+}
+
+func FieldFromIface(val any) *Field {
+	res := &Field{}
+	if m, check := json.IsMap(val); check {
+		res.Name = strings.TrimSpace(m.String("name", ""))
+		if names := m.Slice("fields", nil); len(names) > 0 {
+			res.Names = FieldListFromSlice(names)
+		}
+	} else {
+		if str, check := val.(string); check {
+			res.Name = str
+		}
+	}
+	return res
 }
 
 // Field реализует ...
 type Field struct {
 	Name  string
-	Names []any
+	Names FieldList
+}
+
+func (s *Field) IsEmpty() bool {
+	return len(s.Name) == 0
+}
+
+func (s *Field) IsObject() bool {
+	return len(s.Names) == 0
+}
+
+func (s *Field) String() string {
+	res := "{ " + s.Name
+	if len(s.Names) > 0 {
+		res += " [ "
+		l := make([]string, len(s.Names))
+		for i, name := range s.Names {
+			l[i] = name.String()
+		}
+		res += strings.Join(l, ", ")
+		res += " ] "
+	}
+	res += " }"
+	return res
+}
+
+//////////////////////////////////////////////
+
+func FieldListFromNames(names ...string) FieldList {
+	res := make(FieldList, 0, len(names))
+	for _, name := range names {
+		res = append(res, FieldFromName(name))
+	}
+	return res
+}
+
+func FieldListFromSlice(f []any) FieldList {
+	res := make(FieldList, 0, len(f))
+	for _, v := range f {
+		if fd := FieldFromIface(v); !fd.IsEmpty() {
+			res = append(res, fd)
+		}
+	}
+	return res
+}
+
+func FieldListFromObjects(f ...any) FieldList {
+	return FieldListFromSlice(f)
 }
 
 type FieldList []*Field
@@ -42,19 +110,24 @@ type IFieldChecker interface {
 	RestFieldCheck(fieldName string) bool
 }
 
-func fieldsDefault(t reflect.Type) (res []any) {
+func fieldsDefault(t reflect.Type) (res FieldList) {
 	for i := 0; i < t.NumField(); i++ {
 		fType := t.Field(i)
 		// проверяем тег, если он есть
 		tag := fType.Tag.Get("rest")
 		if len(tag) > 0 && (strings.Contains(tag, "default") || strings.Contains(tag, "fixed")) {
-			res = append(res, fType.Name)
+			res = append(
+				res,
+				&Field{
+					Name: fType.Name,
+				},
+			)
 		}
 	}
 	return
 }
 
-func fieldsFixed(t reflect.Type) (res []any) {
+func fieldsFixed(t reflect.Type) (res FieldNamesList) {
 	for i := 0; i < t.NumField(); i++ {
 		fType := t.Field(i)
 		// проверяем тег, если он есть
@@ -84,10 +157,10 @@ func parseName(val reflect.Value) (res *Field, err error) {
 			err = fmt.Errorf("name is empty")
 			return
 		}
-		fields := jm.Slice("fields", nil)
+		fields := jm.Slice("names", nil)
 		res = &Field{
 			Name:  name,
-			Names: fields,
+			Names: FieldListFromSlice(fields),
 		}
 		return
 	default:
@@ -96,14 +169,14 @@ func parseName(val reflect.Value) (res *Field, err error) {
 	return
 }
 
-func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...any) (res reflect.Value, err IErrorArgs) {
+func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names FieldList) (res reflect.Value, err IErrorArgs) {
 	switch val.Kind() {
 	case reflect.Ptr, reflect.Interface:
-		return fieldVal(val.Elem(), fieldName, files, names...)
+		return fieldVal(val.Elem(), fieldName, files, names)
 	case reflect.Struct:
 		// check fielder interface
 		if f, check := val.Interface().(IRestFielder); check {
-			rVal, err := f.RestFields(fieldName, names...)
+			rVal, err := f.RestFields(fieldName, names)
 			if err != nil {
 				return reflect.Value{}, err
 			}
@@ -113,7 +186,7 @@ func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...
 		if val.CanAddr() {
 			// check fielder interface
 			if f, check := val.Addr().Interface().(IRestFielder); check {
-				rVal, err := f.RestFields(fieldName, names...)
+				rVal, err := f.RestFields(fieldName, names)
 				if err != nil {
 					return reflect.Value{}, err
 				}
@@ -125,19 +198,11 @@ func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...
 			names = fieldsDefault(val.Type())
 		}
 		jm := json.Map{}
-		var fields FieldList
-		addFieldVal := func(name any) bool {
-			rField, rErr := parseName(reflect.ValueOf(name))
-			if rErr != nil {
-				message := fmt.Sprintf("%v: %v", name, err)
-				err = ErrorFiled(fieldName, message)
-				return false
-			}
-			fields = append(fields, rField)
+		addFieldVal := func(name *Field) bool {
 			var field reflect.Value
 			for i := 0; i < val.NumField(); i++ {
 				f := val.Type().Field(i)
-				if f.Name == rField.Name {
+				if f.Name == name.Name {
 					tag := f.Tag.Get("rest")
 					if len(tag) > 0 && strings.Contains(tag, "ignore") {
 						return true
@@ -153,14 +218,16 @@ func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...
 			var fVal reflect.Value
 			if fVal, err = fieldVal(
 				field,
-				fmt.Sprintf("%v.%v", fieldName, rField.Name),
+				fmt.Sprintf("%v.%v", fieldName, name.Name),
 				files,
-				rField.Names...,
+				name.Names,
 			); err != nil {
 				return false
 			}
-			if fVal.IsValid() && !fVal.IsZero() {
-				jm[rField.Name] = fVal.Interface()
+			if fVal.IsValid() {
+				jm[name.Name] = fVal.Interface()
+			} else {
+				jm[name.Name] = nil
 			}
 			return true
 		}
@@ -169,19 +236,20 @@ func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...
 				return
 			}
 		}
+
 		// fields fixed
 		fFixed := fieldsFixed(val.Type())
 		for _, fixed := range fFixed {
-			if !jm.KeyExists(fixed.(string)) {
-				addFieldVal(fixed)
+			if !jm.KeyExists(fixed) {
+				addFieldVal(FieldFromName(fixed))
 			}
 		}
 		// post (когда результирующий объект уже сформирован)
 		if fielder, check := val.Interface().(IFielderPost); check {
-			fielder.RestFieldsPost(jm, files, fields)
+			fielder.RestFieldsPost(jm, files, names)
 		} else if val.CanAddr() {
 			if fielder, check := val.Addr().Interface().(IFielderPost); check {
-				fielder.RestFieldsPost(jm, files, fields)
+				fielder.RestFieldsPost(jm, files, names)
 			}
 		}
 		res = reflect.ValueOf(jm)
@@ -196,7 +264,7 @@ func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...
 				val.Index(i),
 				fName,
 				files,
-				names...,
+				names,
 			)
 			if err != nil {
 				return
@@ -211,7 +279,7 @@ func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...
 			fName := fmt.Sprintf("%v[%v]", fieldName, key.String())
 			var rVal reflect.Value
 			iVal := val.MapIndex(key)
-			rVal, err = fieldVal(iVal, fName, files)
+			rVal, err = fieldVal(iVal, fName, files, nil)
 			if err != nil {
 				return
 			}
@@ -224,9 +292,9 @@ func fieldVal(val reflect.Value, fieldName string, files RequestFiles, names ...
 	}
 }
 
-func FieldsAny(obj any, files RequestFiles, names ...any) (any, IErrorArgs) {
+func FieldsAny(obj any, files RequestFiles, names FieldList) (any, IErrorArgs) {
 	sVal := reflect.ValueOf(obj)
-	rVal, err := fieldVal(sVal.Elem(), "", files, names...)
+	rVal, err := fieldVal(sVal.Elem(), "", files, names)
 	if err != nil {
 		return nil, err
 	}
@@ -234,9 +302,9 @@ func FieldsAny(obj any, files RequestFiles, names ...any) (any, IErrorArgs) {
 }
 
 // Fields позволяет получить значения объекта в json
-func Fields(obj any, files RequestFiles, names ...any) (json.Map, IErrorArgs) {
+func Fields(obj any, files RequestFiles, names FieldList) (json.Map, IErrorArgs) {
 	sVal := reflect.ValueOf(obj)
-	rVal, err := fieldVal(sVal.Elem(), "", files, names...)
+	rVal, err := fieldVal(sVal.Elem(), "", files, names)
 	if err != nil {
 		return nil, err
 	}
@@ -279,20 +347,25 @@ func FieldNames(s any) (FieldNamesList, error) {
 
 /////////////////////////////////////////////
 
-func OutFields(req IRequestIn, obj any, files RequestFiles, names ...any) IRequestOut {
+func OutFields(req IRequestIn, obj any, files RequestFiles, names FieldList) IRequestOut {
 	if files == nil {
 		files = make(RequestFiles)
 	}
-	m, err := Fields(obj, files, names...)
+	if len(names) == 0 {
+		names = req.Fields()
+	}
+	m, err := Fields(obj, files, names)
 	if err != nil {
 		return req.OutError(err)
 	}
 	return req.OutSuccess(m, files)
 }
 
-func OutFieldsReq(req IRequestIn, obj any, files RequestFiles, names ...any) IRequestOut {
+/*
+func OutFieldsReq(req IRequestIn, obj any, files RequestFiles, names FieldList) IRequestOut {
 	if len(names) == 0 {
-		names = req.RData().Slice("fields", nil)
+		names = req.Fields()
 	}
-	return OutFields(req, obj, files, names...)
+	return OutFields(req, obj, files, names)
 }
+*/

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module git.ali33.ru/fcg-xvii/rest
 go 1.20
 
 require (
-	git.ali33.ru/fcg-xvii/go-tools v0.0.0-20230529104008-2552c5121c91
+	git.ali33.ru/fcg-xvii/go-tools v0.0.0-20240727074427-0856f1c40759
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-chi/chi/v5 v5.0.11
 	github.com/go-chi/cors v1.2.1

+ 2 - 0
go.sum

@@ -1,5 +1,7 @@
 git.ali33.ru/fcg-xvii/go-tools v0.0.0-20230529104008-2552c5121c91 h1:8N3j1V1Yx24uHwCp+LPOOdzdoRq3ad9tEIchHd6CZUI=
 git.ali33.ru/fcg-xvii/go-tools v0.0.0-20230529104008-2552c5121c91/go.mod h1:YbBhWFFNNQIKcRisQFnpVaN5KA+XHGImSU1Z/MuntqU=
+git.ali33.ru/fcg-xvii/go-tools v0.0.0-20240727074427-0856f1c40759 h1:pxakIkKkaHBObni4PCZYUaXR74RvNKeV9ShHGHAE/mQ=
+git.ali33.ru/fcg-xvii/go-tools v0.0.0-20240727074427-0856f1c40759/go.mod h1:YbBhWFFNNQIKcRisQFnpVaN5KA+XHGImSU1Z/MuntqU=
 github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
 github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
 github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=

+ 1 - 5
request.go

@@ -30,7 +30,6 @@ type IRequest interface {
 	RFiles() RequestFiles
 	RFile(string) (IReadCloserLen, bool)
 	RClose()
-	Fields(any, RequestFiles) (json.Map, IErrorArgs)
 }
 
 ////////////////////////////////////////////
@@ -73,10 +72,6 @@ func (s *Request) RClose() {
 	s.Files = nil
 }
 
-func (s *Request) Fields(obj any, files RequestFiles) (json.Map, IErrorArgs) {
-	return Fields(obj, files, s.Data.Slice("fields", nil)...)
-}
-
 func (s *Request) ParseOffset() int {
 	return s.Data.Int32("offset", 0)
 }
@@ -99,6 +94,7 @@ type IRequestIn interface {
 	OutError(err IErrorArgs) IRequestOut
 	ClientData(key string) (any, bool)
 	SetClientData(key string, data any)
+	Fields() FieldList
 }
 
 type RequestIn struct {

+ 4 - 0
rest_http/request_in.go

@@ -28,3 +28,7 @@ func (s *RequestIn) OutSuccess(data json.Map, files rest.RequestFiles) rest.IReq
 func (s *RequestIn) OutError(err rest.IErrorArgs) rest.IRequestOut {
 	return ResponseError(err)
 }
+
+func (s *RequestIn) Fields() rest.FieldList {
+	return rest.FieldListFromSlice(s.RData().Slice("fields", nil))
+}

+ 4 - 0
rest_websocket/request.go

@@ -44,6 +44,10 @@ func (s *RequestIn) SetClientData(key string, data any) {
 	s.owner.clientData[key] = data
 }
 
+func (s *RequestIn) Fields() rest.FieldList {
+	return rest.FieldListFromSlice(s.RData().Slice("fields", nil))
+}
+
 func (s *RequestIn) OutSuccess(data json.Map, files rest.RequestFiles) rest.IRequestOut {
 	return &rest.RequestStream{
 		ID: s.ID,

+ 92 - 11
z_test.go

@@ -19,6 +19,7 @@ func TestFieldsDefault(t *testing.T) {
 
 	rr := RR{}
 	fields := fieldsDefault(reflect.TypeOf(rr))
+	//log.Println(*fields[0])
 	expected := []any{"id", "name"}
 	if len(fields) != len(expected) {
 		t.Errorf("expected %v, real %v", expected, fields)
@@ -30,6 +31,77 @@ func TestFieldsDefault(t *testing.T) {
 	}
 }
 
+//////////////////////////////////////////////////
+
+type FPermissions struct {
+	ID       int `rest:"default"`
+	ParentID int
+	Name     string
+}
+
+type FUser struct {
+	ID     int
+	Name   string
+	Group  *FPermissions
+	Groups []*FPermissions
+}
+
+func TestFieldsList(t *testing.T) {
+	u := &FUser{
+		ID:   1,
+		Name: "okko",
+		/*
+			Group: &FPermissions{
+				ID:       100,
+				ParentID: 50,
+				Name:     "OGroup",
+			},
+		*/
+		Groups: []*FPermissions{
+			{
+				ID:       100,
+				ParentID: 5,
+				Name:     "OGroup",
+			},
+			{
+				ID:       200,
+				ParentID: 0,
+				Name:     "GGroup",
+			},
+		},
+	}
+
+	fList := FieldList{
+		FieldFromName("ID"),
+		FieldFromName("Name"),
+		&Field{
+			Name: "Group",
+			Names: []*Field{
+				{
+					Name: "ID",
+				},
+			},
+		},
+		&Field{
+			Name: "Groups",
+			Names: []*Field{
+				{
+					Name: "ParentID",
+				},
+				{
+					Name: "Name",
+				},
+			},
+		},
+	}
+
+	m, err := Fields(u, nil, fList)
+	if err != nil {
+		t.Error(err)
+	}
+	m.LogPretty()
+}
+
 func TestFielderName(t *testing.T) {
 	name := "Name"
 	if field, err := parseName(reflect.ValueOf(name)); field.Name != name || err != nil {
@@ -109,10 +181,7 @@ func TestFielderVal(t *testing.T) {
 		},
 	}
 
-	val, err := fieldVal(
-		reflect.ValueOf(rr),
-		"",
-		nil,
+	fList := FieldListFromObjects(
 		"id",
 		"name",
 		"nums",
@@ -127,6 +196,13 @@ func TestFielderVal(t *testing.T) {
 			"fields": []any{"id"},
 		},
 	)
+
+	val, err := fieldVal(
+		reflect.ValueOf(rr),
+		"",
+		nil,
+		fList,
+	)
 	if err != nil {
 		t.Error(err, err.Args())
 	}
@@ -134,9 +210,7 @@ func TestFielderVal(t *testing.T) {
 	m := val.Interface().(json.Map)
 	m.LogPretty()
 
-	mVal, err := Fields(
-		rr,
-		nil,
+	rfList := FieldListFromObjects(
 		"id",
 		"name",
 		"nums",
@@ -151,6 +225,12 @@ func TestFielderVal(t *testing.T) {
 			"fields": []any{"id"},
 		},
 	)
+
+	mVal, err := Fields(
+		rr,
+		nil,
+		rfList,
+	)
 	if err != nil {
 		t.Error(err, err.Args())
 	}
@@ -171,7 +251,7 @@ func TestTime(t *testing.T) {
 		Created: RTime{Time(time.Now())},
 	}
 	log.Println(obj)
-	f, _ := Fields(&obj, nil)
+	f, _ := Fields(&obj, nil, nil)
 	f.LogPretty()
 
 	var rObj TimeObj
@@ -199,7 +279,8 @@ type LList struct {
 
 func (s *LList) RestFields(fieldName string, names ...any) (any, IErrorArgs) {
 	if fieldName == "" {
-		l, err := FieldsAny(&s.Result, nil, names...)
+		fList := FieldListFromObjects(names...)
+		l, err := FieldsAny(&s.Result, nil, fList)
 		if err != nil {
 			return nil, err
 		}
@@ -225,7 +306,7 @@ func TestFielderList(t *testing.T) {
 		},
 	}
 
-	m, err := Fields(obj, nil, "id")
+	m, err := Fields(obj, nil, FieldListFromObjects("id"))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -361,5 +442,5 @@ func TestSerializeEmail(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	t.Log(r)
+	t.Log(r, *r.UUser)
 }