From 5716323e3ade1884d428de888c3fb4d8b55a63ff Mon Sep 17 00:00:00 2001 From: Denis Zinovyev Date: Wed, 24 Feb 2021 21:54:15 +0300 Subject: [PATCH 1/2] required_group for parameters --- .gitignore | 3 ++ reflect/reflect.go | 30 ++++++++++------ reflect/reflect_struct_test.go | 62 ++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c03b52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +### IDE ### +.idea +.vscode diff --git a/reflect/reflect.go b/reflect/reflect.go index f7da151..09c46bb 100644 --- a/reflect/reflect.go +++ b/reflect/reflect.go @@ -15,16 +15,17 @@ type Options struct { // Item - type Item struct { - Name string `json:"name"` - Value interface{} `json:"-"` - Enum interface{} `json:"enum"` - Type string `json:"type"` - MetaType string `json:"meta_type"` - Tags string `json:"tags"` - Comment string `json:"comment"` - Required bool `json:"required"` - Deprecated bool `json:"deprecated"` - Nested []Item `json:"nested"` + Name string `json:"name"` + Value interface{} `json:"-"` + Enum interface{} `json:"enum"` + Type string `json:"type"` + MetaType string `json:"meta_type"` + Tags string `json:"tags"` + Comment string `json:"comment"` + Required bool `json:"required"` + RequiredGroup string `json:"required_group"` + Deprecated bool `json:"deprecated"` + Nested []Item `json:"nested"` } // Get - @@ -111,6 +112,7 @@ var ( reIsPrivate = regexp.MustCompile(`^[a-z]`) reJsonTagName = regexp.MustCompile(`^[^,]+`) reOmitEmpty = regexp.MustCompile(`^(.+,)?omitempty(,.+)?$`) + reRequired = regexp.MustCompile(`^(true(,([\w]+))?)|(false)$`) ) func initNested(o Options, typeRef reflect.Type, valRef reflect.Value) []Item { @@ -152,6 +154,14 @@ func initNested(o Options, typeRef reflect.Type, valRef reflect.Value) []Item { item.Required = f.Tag.Get("required") == "true" item.Deprecated = f.Tag.Get("deprecated") == "true" + required := f.Tag.Get("required") + if reRequired.MatchString(required) { + item.Required = strings.HasPrefix(required, "true") + if attrs := strings.Split(required, ","); len(attrs) > 1 { + item.RequiredGroup = attrs[1] + } + } + td := zfv.MethodByName("TypeDescription") if td.IsValid() { c := td.Call([]reflect.Value{})[0].String() diff --git a/reflect/reflect_struct_test.go b/reflect/reflect_struct_test.go index 9847a55..3d1439a 100644 --- a/reflect/reflect_struct_test.go +++ b/reflect/reflect_struct_test.go @@ -1,6 +1,7 @@ package reflect_test import ( + "encoding/json" "testing" "github.com/gothing/draft/reflect" @@ -27,6 +28,67 @@ func TestStructWithJSONTag(t *testing.T) { assert.Equal(t, []string{"access_token", "ClientID"}, v.Keys()) } +type StructWithRequiredTags struct { + RequiredField int `required:"true"` + RequiredField1 string `required:"true,grp"` + RequiredField2 uint64 `required:"true,grp"` + OptionalField uint `required:"false"` + ExtraField bool +} + +func TestStructWithRequiredTag(t *testing.T) { + v := reflect.Get( + StructWithRequiredTags{}, + reflect.Options{ + SnakeCase: true, + }, + ) + + jsonObj, err := json.Marshal(v) + assert.NoError(t, err) + + var m map[string]interface{} + assert.NoError(t, json.Unmarshal(jsonObj, &m)) + + fields := []struct { + Name string + Required bool + RequiredGroup string + }{ + { + Name: "required_field", + Required: true, + }, + { + Name: "required_field1", + Required: true, + RequiredGroup: "grp", + }, + { + Name: "required_field2", + Required: true, + RequiredGroup: "grp", + }, + { + Name: "optional_field", + }, + { + Name: "extra_field", + }, + } + + for _, field := range fields { + nested := m["nested"].([]interface{}) + for _, f := range nested { + f := f.(map[string]interface{}) + if f["name"] == field.Name { + assert.Equal(t, field.Required, f["required"]) + assert.Equal(t, field.RequiredGroup, f["required_group"]) + } + } + } +} + func xTestStructComposed(t *testing.T) { v := reflect.Get(StructComposed{}, reflect.Options{ SnakeCase: true, From c368cf7564d00bb6efb865c136879b81348de912 Mon Sep 17 00:00:00 2001 From: Denis Zinovyev Date: Sat, 27 Mar 2021 19:25:16 +0300 Subject: [PATCH 2/2] required_group in mocks --- endpoint.go | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/endpoint.go b/endpoint.go index a6cdfdb..15d434f 100644 --- a/endpoint.go +++ b/endpoint.go @@ -77,24 +77,42 @@ func (e *Endpoint) GetEndpointMock(r *Request) interface{} { idx := -1 weight := -1 cases := e.endpointScheme.Cases() - missed := []string{} + var missed []string for i, c := range cases { if Status.OK == c.Status { w := 0 ref := reflect.Get(c.Params, reflect.Options{SnakeCase: true}) m := make([]string, 0, len(ref.Nested)) + reqByGrp := make(map[string]bool) for _, item := range ref.Nested { - if !r.Params.Has(item.Name) && item.Required { - m = append(m, item.Name) + if item.RequiredGroup != "" { + reqByGrp[item.RequiredGroup] = false } + } + for _, item := range ref.Nested { + if item.Required { + if r.Params.Has(item.Name) { + if item.RequiredGroup != "" { + reqByGrp[item.RequiredGroup] = true + } + } else if _, ok := reqByGrp[item.RequiredGroup]; !ok { + m = append(m, item.Name) + } + } if r.Params.Get(item.Name) == fmt.Sprintf("%v", item.Value) { w++ } } + for group, present := range reqByGrp { + if !present { + m = append(m, fmt.Sprintf("[%s]", group)) + } + } + if len(m) > 0 && len(missed) == 0 { missed = m }