diff --git a/pkg/schema/field/array.go b/pkg/schema/field/array.go index 6700eb9b09af7bb714566dca8c82e90d026c7d57..32d6f72f5e9260857dbabb4d46e6e498242fab4d 100644 --- a/pkg/schema/field/array.go +++ b/pkg/schema/field/array.go @@ -116,17 +116,17 @@ func (ArrayType) Walk(ctx context.Context, field *Field, v interface{}, fn WalkF return nil, false, nil } - // Выполняется обход по схеме - if opts.WalkSchema && v == nil { - params.Item.Walk(ctx, v, fn, WalkOpts(opts)) - return nil, false, nil - } - arr, ok := v.([]interface{}) - if !ok { + if !ok && v != nil { return nil, false, fmt.Errorf("incorrect type: \"%s\", expected \"[]interface{}\"", reflect.ValueOf(v).Kind()) } + // Выполняется обход по схеме + if opts.WalkSchema && len(arr) == 0 { + _, _, _ = params.Item.Walk(ctx, nil, fn, WalkOpts(opts)) + return nil, false, nil + } + m := make([]interface{}, 0, len(arr)) var merr *multierror.Error diff --git a/pkg/schema/field/array_test.go b/pkg/schema/field/array_test.go index 94e60258661932dc72c2c34c27ea7cecb5c6b33d..964075c3e70b046b942057e837dd59c0421aecfe 100644 --- a/pkg/schema/field/array_test.go +++ b/pkg/schema/field/array_test.go @@ -1,6 +1,7 @@ package field import ( + "context" "fmt" "testing" @@ -23,6 +24,26 @@ func TestArrayField_Decode(t *testing.T) { []interface{}{1.0, 2.0}, false, }, + { + "With object inside with nil-data", + Array( + Object("a", String(), + "b", String()), + ), + nil, + nil, + false, + }, + { + "With object inside with data", + Array( + Object("a", String(), + "b", String()), + ), + []interface{}{map[string]interface{}{"a": "1", "b": "2"}}, + []interface{}{map[string]interface{}{"a": "1", "b": "2"}}, + false, + }, { "Incorrect type", Array(Number("int")), @@ -83,3 +104,61 @@ func TestArrayField_Encode(t *testing.T) { }) } } + +func TestArrayType_Walk(t *testing.T) { + tests := []struct { + name string + field *Field + v interface{} + fn WalkFunc + opts *WalkOptions + want interface{} + want1 bool + wantErr assert.ErrorAssertionFunc + }{ + { + name: "With nil data and WalkSchema = false", + field: Array(Object("a", String(), "b", String())), + v: nil, + opts: &WalkOptions{WalkSchema: false}, + want: nil, + want1: false, + wantErr: assert.NoError, + }, + { + name: "With empty data and WalkSchema = false", + field: Array(Object("a", String(), "b", String())), + v: []interface{}{map[string]interface{}{}}, + opts: &WalkOptions{WalkSchema: false}, + fn: func(ctx context.Context, fld *Field, v interface{}) (result WalkFuncResult, err error) { + return WalkFuncResult{}, err + }, + want: []interface{}{map[string]interface{}{}}, + want1: false, + wantErr: assert.NoError, + }, + { + name: "With data and WalkSchema = false", + field: Array(Object("a", String(), "b", String())), + v: []interface{}{map[string]interface{}{"a": "1", "b": "2"}}, + opts: &WalkOptions{WalkSchema: false}, + fn: func(ctx context.Context, fld *Field, v interface{}) (result WalkFuncResult, err error) { + return WalkFuncResult{}, err + }, + want: []interface{}{map[string]interface{}{"a": "1", "b": "2"}}, + want1: false, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ar := ArrayType{} + got, got1, err := ar.Walk(context.Background(), tt.field, tt.v, tt.fn, tt.opts) + if !tt.wantErr(t, err, fmt.Sprintf("Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts)) { + return + } + assert.Equalf(t, tt.want, got, "Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts) + assert.Equalf(t, tt.want1, got1, "Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts) + }) + } +} diff --git a/pkg/schema/test/object_test.go b/pkg/schema/test/object_test.go index 38868f66989108c142e50eaad62539be381edb4f..8ba60d9d39857f64af77ce14610b698049548093 100644 --- a/pkg/schema/test/object_test.go +++ b/pkg/schema/test/object_test.go @@ -1343,6 +1343,60 @@ func TestSchema_Introspect(t *testing.T) { []string{"object_a", "field1", "field2"}, false, }, + { + // если у объекта нет данных Introspect возвращает все поля + "With not initialized object in data", + map[string]interface{}{"object": []interface{}{}}, + schema.New( + "object", field.Array( + field.Object( + "a", field.String(), + "b", field.String(), + "c", field.String(), + ), + ).WithUI(&field.UI{Widget: "Tags"}), + ), + map[string]interface{}{"object": []interface{}{}}, + []string{"object.a", "object.b", "object.c"}, + []string{}, + false, + }, + { + // при добавлении значения по умолчанию для поля запрос Introspect возвращает все поля + "Object initialized by modify.Default in schema", + map[string]interface{}{}, + schema.New( + "object", field.Array( + field.Object( + "a", field.String(), + "b", field.String(), + "c", field.String(), + ), + ).AddOptions(modify.Default([]interface{}{map[string]interface{}{}})).WithUI(&field.UI{Widget: "Tags"}), + ), + map[string]interface{}{}, + []string{"object.a", "object.b", "object.c"}, + []string{}, + false, + }, + { + // при добавлении пустого объекта перед запросом Introspect возвращаются все поля + "Object initialized in data", + map[string]interface{}{"object": []interface{}{map[string]interface{}{}}}, + schema.New( + "object", field.Array( + field.Object( + "a", field.String(), + "b", field.String(), + "c", field.String(), + ), + ).WithUI(&field.UI{Widget: "Tags"}), + ), + map[string]interface{}{"object": []interface{}{map[string]interface{}{}}}, + []string{"object.a", "object.b", "object.c"}, + []string{}, + false, + }, } ctx := context.Background()