From 2f684ea678c0bcd59b8faed69cace00c9a4d29e6 Mon Sep 17 00:00:00 2001
From: Alex Petraky <petraky@perx.ru>
Date: Thu, 20 Jun 2024 10:12:57 +0000
Subject: [PATCH] =?UTF-8?q?fix(core):=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=20?=
 =?UTF-8?q?=D1=81=D1=85=D0=B5=D0=BC=D1=8B=20Walk=20=D0=B4=D0=BB=D1=8F=20?=
 =?UTF-8?q?=D1=82=D0=B8=D0=BF=D0=B0=20Array,=20=D1=80=D0=B5=D1=88=D0=B5?=
 =?UTF-8?q?=D0=BD=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0=20=D0=BE?=
 =?UTF-8?q?=D1=82=D1=81=D1=83=D1=82=D1=81=D1=82=D0=B2=D0=B8=D1=8F=20=D1=81?=
 =?UTF-8?q?=D1=85=D0=B5=D0=BC=D1=8B=20=D0=BF=D1=80=D0=B8=20=D0=B2=D1=8B?=
 =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B5=20Introspect?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pkg/schema/field/array.go      | 14 +++---
 pkg/schema/field/array_test.go | 79 ++++++++++++++++++++++++++++++++++
 pkg/schema/test/object_test.go | 54 +++++++++++++++++++++++
 3 files changed, 140 insertions(+), 7 deletions(-)

diff --git a/pkg/schema/field/array.go b/pkg/schema/field/array.go
index 6700eb9b..32d6f72f 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 94e60258..964075c3 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 38868f66..8ba60d9d 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()
-- 
GitLab