Skip to content

Commit 015abb3

Browse files
committed
Merge branch '411-database-list' into 'master'
fix(engine): the database list creates a key in the config on save if not present (#411) Closes #411 See merge request postgres-ai/database-lab!583
2 parents 833be8d + 63cdf2f commit 015abb3

File tree

9 files changed

+137
-40
lines changed

9 files changed

+137
-40
lines changed

engine/pkg/models/configuration.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type ConfigProjection struct {
2121
Password *string `proj:"retrieval.spec.logicalDump.options.source.connection.password" groups:"sensitive"`
2222
Port *int64 `proj:"retrieval.spec.logicalDump.options.source.connection.port"`
2323
Username *string `proj:"retrieval.spec.logicalDump.options.source.connection.username"`
24-
DBList map[string]interface{} `proj:"retrieval.spec.logicalDump.options.databases"`
24+
DBList map[string]interface{} `proj:"retrieval.spec.logicalDump.options.databases,createKey"`
2525
DumpParallelJobs *int64 `proj:"retrieval.spec.logicalDump.options.parallelJobs"`
2626
RestoreParallelJobs *int64 `proj:"retrieval.spec.logicalRestore.options.parallelJobs"`
2727
}

engine/pkg/util/projection/common_test.go

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package projection
22

33
import (
4+
"strconv"
45
"testing"
56

67
"github.com/stretchr/testify/require"
@@ -14,10 +15,12 @@ func getJSONNormal() map[string]interface{} {
1415
"int": int64(1),
1516
"float": 1.1,
1617
"bool": true,
18+
"create": "create",
1719
"ptrString": "string",
1820
"ptrInt": int64(1),
1921
"ptrFloat": 1.1,
2022
"ptrBool": true,
23+
"ptrCreate": "ptrCreate",
2124
},
2225
}
2326
}
@@ -43,10 +46,12 @@ nested:
4346
int: 1
4447
float: 1.1
4548
bool: true
49+
create: create
4650
ptrString: "string"
4751
ptrInt: 1
4852
ptrFloat: 1.1
4953
ptrBool: true
54+
ptrCreate: ptrCreate
5055
`
5156

5257
const yamlNull = `
@@ -55,10 +60,12 @@ nested:
5560
int: null
5661
float: null
5762
bool: null
63+
create: null
5864
ptrString: null
5965
ptrInt: null
6066
ptrFloat: null
6167
ptrBool: null
68+
ptrCreate: null
6269
`
6370

6471
const yamlDiverted = `
@@ -67,10 +74,12 @@ nested:
6774
int: 200
6875
float: 200.2
6976
bool: false
77+
create: "to be stored"
7078
ptrString: "to be stored"
7179
ptrInt: 200
7280
ptrFloat: 200.2
7381
ptrBool: false
82+
ptrCreate: "to be stored"
7483
`
7584

7685
const yamlNullApplied = `
@@ -79,10 +88,12 @@ nested:
7988
int: 0
8089
float: 0.0
8190
bool: false
91+
create: ""
8292
ptrString: "string"
8393
ptrInt: 1
8494
ptrFloat: 1.1
8595
ptrBool: true
96+
ptrCreate: "ptrCreate"
8697
`
8798

8899
type testStruct struct {
@@ -101,6 +112,9 @@ type testStruct struct {
101112

102113
PtrMissField *string `proj:"nested.ptrMiss"`
103114
PtrMissNestedField *string `proj:"nested.ptrMissMap.nested"`
115+
116+
CreateField string `proj:"nested.create,createKey"`
117+
PtrCreateField *string `proj:"nested.ptrCreate,createKey"`
104118
}
105119

106120
func fullTestStruct() *testStruct {
@@ -110,20 +124,24 @@ func fullTestStruct() *testStruct {
110124
boolField := true
111125
missField := "ptrMiss"
112126
missNestedField := "ptrMissNested"
127+
ptrCreateField := "ptrCreate"
113128

114129
return &testStruct{
115-
StringField: "string",
116-
IntField: int64(1),
117-
FloatField: 1.1,
118-
BoolField: true,
119-
MissField: "miss",
120-
MissNestedField: "missNested",
130+
StringField: "string",
131+
IntField: int64(1),
132+
FloatField: 1.1,
133+
BoolField: true,
134+
MissField: "miss",
135+
MissNestedField: "missNested",
136+
CreateField: "create",
137+
121138
PtrStringField: &strField,
122139
PtrIntField: &intField,
123140
PtrFloatField: &floatField,
124141
PtrBoolField: &boolField,
125142
PtrMissField: &missField,
126143
PtrMissNestedField: &missNestedField,
144+
PtrCreateField: &ptrCreateField,
127145
}
128146
}
129147

@@ -195,13 +213,39 @@ func requireYamlNormal(t *testing.T, node *yaml.Node) {
195213
normal := &yaml.Node{}
196214
err := yaml.Unmarshal([]byte(yamlNormal), normal)
197215
require.NoError(t, err)
198-
require.EqualValues(t, normal, node)
216+
requireNodeEquals(t, normal, node)
199217
}
200218

201219
func requireYamlNullApplied(t *testing.T, node *yaml.Node) {
202220
t.Helper()
203221
null := &yaml.Node{}
204222
err := yaml.Unmarshal([]byte(yamlNullApplied), null)
205223
require.NoError(t, err)
206-
require.EqualValues(t, null, node)
224+
requireNodeEquals(t, null, node)
225+
}
226+
227+
func requireNodeEquals(t *testing.T, left *yaml.Node, right *yaml.Node) {
228+
t.Helper()
229+
230+
var dive func(left, right *yaml.Node, path string)
231+
232+
dive = func(left, right *yaml.Node, path string) {
233+
if left.Kind != right.Kind {
234+
t.Errorf("Kind %d != %d, at path %s", left.Kind, right.Kind, path)
235+
}
236+
if left.Tag != right.Tag {
237+
t.Errorf("Tag %s != %s, at path %s", left.Tag, right.Tag, path)
238+
}
239+
if left.Value != right.Value {
240+
t.Errorf("Value %s != %s, at path %s", left.Value, right.Value, path)
241+
}
242+
if len(left.Content) != len(right.Content) {
243+
t.Errorf("Content length %d != %d, at path %s", len(left.Content), len(right.Content), path)
244+
}
245+
for i := range left.Content {
246+
dive(left.Content[i], right.Content[i], path+"."+strconv.Itoa(i))
247+
}
248+
}
249+
250+
dive(left, right, "")
207251
}

engine/pkg/util/projection/json.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ func NewSoftJSON(root map[string]interface{}) Accessor {
1515
return &softJSON{root: root}
1616
}
1717

18-
func (s *softJSON) Set(path []string, value interface{}, _ ptypes.Type) error {
18+
func (s *softJSON) Set(field FieldSet) error {
1919
parent := s.root
20-
for _, key := range path[:len(path)-1] {
20+
for _, key := range field.Path[:len(field.Path)-1] {
2121
child, hasChild := parent[key]
2222
if !hasChild {
2323
child = make(map[string]interface{})
@@ -32,11 +32,11 @@ func (s *softJSON) Set(path []string, value interface{}, _ ptypes.Type) error {
3232
}
3333
}
3434

35-
key := path[len(path)-1]
35+
key := field.Path[len(field.Path)-1]
3636

3737
child, ok := parent[key]
3838
if !ok {
39-
parent[key] = value
39+
parent[key] = field.Value
4040
return nil
4141
}
4242

@@ -46,15 +46,15 @@ func (s *softJSON) Set(path []string, value interface{}, _ ptypes.Type) error {
4646
case []interface{}:
4747
return fmt.Errorf("node is already a sequence node")
4848
default:
49-
parent[key] = value
49+
parent[key] = field.Value
5050
}
5151

5252
return nil
5353
}
5454

55-
func (s *softJSON) Get(path []string, t ptypes.Type) (interface{}, error) {
55+
func (s *softJSON) Get(field FieldGet) (interface{}, error) {
5656
parent := s.root
57-
for _, key := range path[:len(path)-1] {
57+
for _, key := range field.Path[:len(field.Path)-1] {
5858
child, hasChild := parent[key]
5959
if !hasChild {
6060
return nil, nil
@@ -68,7 +68,7 @@ func (s *softJSON) Get(path []string, t ptypes.Type) (interface{}, error) {
6868
}
6969
}
7070

71-
key := path[len(path)-1]
71+
key := field.Path[len(field.Path)-1]
7272

7373
child, ok := parent[key]
7474
if !ok {
@@ -79,7 +79,7 @@ func (s *softJSON) Get(path []string, t ptypes.Type) (interface{}, error) {
7979
return nil, nil
8080
}
8181

82-
typed, err := ptypes.Convert(child, t)
82+
typed, err := ptypes.Convert(child, field.Type)
8383
if err != nil {
8484
return nil, fmt.Errorf("failed to convert %#v: %w", child, err)
8585
}

engine/pkg/util/projection/operations.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ func Load(target interface{}, accessor Accessor, options LoadOptions) error {
1515
return nil
1616
}
1717

18-
accessorValue, err := accessor.Get(tag.path, tag.fType)
18+
accessorValue, err := accessor.Get(FieldGet{
19+
Path: tag.path, Type: tag.fType,
20+
})
1921
if err != nil {
2022
return err
2123
}
@@ -53,7 +55,10 @@ func Store(target interface{}, accessor Accessor, options StoreOptions) error {
5355
} else {
5456
accessorValue = field.Interface()
5557
}
56-
err := accessor.Set(tag.path, accessorValue, tag.fType)
58+
err := accessor.Set(FieldSet{
59+
Path: tag.path, Value: accessorValue, Type: tag.fType,
60+
CreateKey: tag.createKey,
61+
})
5762
if err != nil {
5863
return err
5964
}

engine/pkg/util/projection/store_json_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ func TestStoreJson(t *testing.T) {
2424
"missMap": map[string]interface{}{
2525
"nested": "missNested",
2626
},
27+
"create": "create",
28+
2729
"ptrString": "string",
2830
"ptrInt": int64(1),
2931
"ptrFloat": 1.1,
@@ -32,6 +34,7 @@ func TestStoreJson(t *testing.T) {
3234
"ptrMissMap": map[string]interface{}{
3335
"nested": "ptrMissNested",
3436
},
37+
"ptrCreate": "ptrCreate",
3538
},
3639
}
3740
r.EqualValues(expected, node)
@@ -55,11 +58,13 @@ func TestStoreJsonNull(t *testing.T) {
5558
"missMap": map[string]interface{}{
5659
"nested": "",
5760
},
61+
"create": "",
5862

5963
"ptrString": "string",
6064
"ptrInt": int64(1),
6165
"ptrFloat": 1.1,
6266
"ptrBool": true,
67+
"ptrCreate": "ptrCreate",
6368
},
6469
}
6570

engine/pkg/util/projection/tags.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ import (
66

77
"github.com/pkg/errors"
88

9+
"gitlab.com/postgres-ai/database-lab/v3/pkg/util"
910
"gitlab.com/postgres-ai/database-lab/v3/pkg/util/ptypes"
1011
)
1112

1213
const projectionTag = "proj"
1314
const projectionGroupTag = "groups"
1415

1516
type fieldTag struct {
16-
path []string
17-
groups []string
18-
isPtr bool
19-
fType ptypes.Type
17+
path []string
18+
groups []string
19+
isPtr bool
20+
fType ptypes.Type
21+
createKey bool
2022
}
2123

2224
// LoadOptions is used to filter fields to load.
@@ -38,6 +40,8 @@ func getFieldTag(value reflect.StructField) (*fieldTag, error) {
3840
options := strings.Split(tag, ",")
3941
path := strings.Split(options[0], ".")
4042

43+
createKey := util.IncludesString(options, "createKey")
44+
4145
var isPtr bool
4246

4347
var fType ptypes.Type
@@ -64,10 +68,11 @@ func getFieldTag(value reflect.StructField) (*fieldTag, error) {
6468
}
6569

6670
return &fieldTag{
67-
path: path,
68-
fType: fType,
69-
isPtr: isPtr,
70-
groups: groups,
71+
path: path,
72+
fType: fType,
73+
isPtr: isPtr,
74+
groups: groups,
75+
createKey: createKey,
7176
}, nil
7277
}
7378

engine/pkg/util/projection/types.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,22 @@ package projection
22

33
import "gitlab.com/postgres-ai/database-lab/v3/pkg/util/ptypes"
44

5+
// FieldSet represents a field to be set in the accessor.
6+
type FieldSet struct {
7+
Path []string
8+
Value interface{}
9+
Type ptypes.Type
10+
CreateKey bool
11+
}
12+
13+
// FieldGet is used to retrieve a field value from an accessor.
14+
type FieldGet struct {
15+
Path []string
16+
Type ptypes.Type
17+
}
18+
519
// Accessor is an interface for getting and setting values from a json / yaml / anything else
620
type Accessor interface {
7-
Set(path []string, value interface{}, t ptypes.Type) error
8-
Get(path []string, t ptypes.Type) (interface{}, error)
21+
Set(set FieldSet) error
22+
Get(get FieldGet) (interface{}, error)
923
}

0 commit comments

Comments
 (0)