Skip to content

Commit e9794d1

Browse files
authored
Merge pull request #18 from dipdup-io/GO-80-refine-MakeComments-with-struct-composition
2 parents 9c90241 + 7c374bd commit e9794d1

File tree

2 files changed

+215
-10
lines changed

2 files changed

+215
-10
lines changed

database/comment.go

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package database
33
import (
44
"context"
55
"github.com/dipdup-net/go-lib/hasura"
6+
"github.com/pkg/errors"
67
"reflect"
78
"strings"
89
)
@@ -47,26 +48,65 @@ func MakeComments(ctx context.Context, sc SchemeCommenter, models ...interface{}
4748
continue
4849
}
4950

50-
comment, ok := getComment(fieldType)
51-
if !ok || comment == "" {
51+
if fieldType.Anonymous {
52+
if err := makeEmbeddedComments(ctx, sc, tableName, fieldType.Type); err != nil {
53+
return err
54+
}
5255
continue
5356
}
5457

55-
columnName, ok := getPgName(fieldType)
56-
57-
if columnName == "-" {
58-
continue
58+
if err := makeFieldComment(ctx, sc, tableName, fieldType); err != nil {
59+
return err
5960
}
61+
}
62+
}
63+
return nil
64+
}
6065

61-
if !ok {
62-
columnName = hasura.ToSnakeCase(fieldType.Name)
63-
}
66+
func makeEmbeddedComments(ctx context.Context, sc SchemeCommenter, tableName string, t reflect.Type) error {
67+
for i := 0; i < t.NumField(); i++ {
68+
fieldType := t.Field(i)
6469

65-
if err := sc.MakeColumnComment(ctx, tableName, columnName, comment); err != nil {
70+
if fieldType.Anonymous {
71+
if err := makeEmbeddedComments(ctx, sc, tableName, fieldType.Type); err != nil {
6672
return err
6773
}
74+
75+
continue
76+
}
77+
78+
if fieldType.Name == "tableName" {
79+
return errors.New("Embedded type must not have tableName field.")
80+
}
81+
82+
if err := makeFieldComment(ctx, sc, tableName, fieldType); err != nil {
83+
return err
6884
}
6985
}
86+
87+
return nil
88+
}
89+
90+
func makeFieldComment(ctx context.Context, sc SchemeCommenter, tableName string, fieldType reflect.StructField) error {
91+
comment, ok := getComment(fieldType)
92+
if !ok || comment == "" {
93+
return nil
94+
}
95+
96+
columnName, ok := getPgName(fieldType)
97+
98+
if columnName == "-" {
99+
return nil
100+
}
101+
102+
if !ok {
103+
columnName = hasura.ToSnakeCase(fieldType.Name)
104+
}
105+
106+
if err := sc.MakeColumnComment(ctx, tableName, columnName, comment); err != nil {
107+
return err
108+
}
109+
70110
return nil
71111
}
72112

database/comment_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,168 @@ func TestMakeCommentsIgnoreNoModels(t *testing.T) {
296296
// Assert
297297
assert.Empty(t, err)
298298
}
299+
300+
func TestMakeCommentsWithStructCompositionAndCorrectOrder(t *testing.T) {
301+
type Operation struct {
302+
CreatedAt int64 `json:"-" comment:"Date of creation in seconds since UNIX epoch."`
303+
UpdatedAt int64 `json:"-" comment:"Date of last update in seconds since UNIX epoch."`
304+
Network string `json:"network" pg:",pk" comment:"Identifies belonging network."`
305+
}
306+
307+
type Ballot struct {
308+
//nolint
309+
tableName struct{} `pg:"ballots" comment:"This table name comment"`
310+
Operation
311+
Ballot string `json:"ballot" comment:"This is field comment"`
312+
}
313+
314+
mockCtrl, mockSC, ctx := initMocks(t)
315+
defer mockCtrl.Finish()
316+
317+
model := Ballot{}
318+
319+
// Assert prepare
320+
tableNameCall := mockSC.
321+
EXPECT().
322+
MakeTableComment(ctx, "ballots", "This table name comment").
323+
Times(1).
324+
Return(nil)
325+
326+
createdAtCall := mockSC.
327+
EXPECT().
328+
MakeColumnComment(ctx, "ballots", "created_at", "Date of creation in seconds since UNIX epoch.").
329+
After(tableNameCall).
330+
Times(1).
331+
Return(nil)
332+
333+
updatedAtCall := mockSC.
334+
EXPECT().
335+
MakeColumnComment(ctx, "ballots", "updated_at", "Date of last update in seconds since UNIX epoch.").
336+
After(createdAtCall).
337+
Times(1).
338+
Return(nil)
339+
340+
networkCall := mockSC.
341+
EXPECT().
342+
MakeColumnComment(ctx, "ballots", "network", "Identifies belonging network.").
343+
After(updatedAtCall).
344+
Times(1).
345+
Return(nil)
346+
347+
mockSC.
348+
EXPECT().
349+
MakeColumnComment(ctx, "ballots", "ballot", "This is field comment").
350+
After(networkCall).
351+
Times(1).
352+
Return(nil)
353+
354+
// Act
355+
err := MakeComments(ctx, mockSC, model)
356+
357+
// Assert
358+
assert.Empty(t, err)
359+
}
360+
361+
func TestMakeCommentsWithDeepStructComposition(t *testing.T) {
362+
type CreatedMetadata struct {
363+
CreatedAt int64 `json:"-" comment:"Date of creation in seconds since UNIX epoch."`
364+
}
365+
366+
type UpdatedMetadata struct {
367+
CreatedMetadata
368+
UpdatedAt int64 `json:"-" comment:"Date of last update in seconds since UNIX epoch."`
369+
}
370+
371+
type Operation struct {
372+
UpdatedMetadata
373+
Network string `json:"network" pg:",pk" comment:"Identifies belonging network."`
374+
}
375+
376+
type Ballot struct {
377+
//nolint
378+
tableName struct{} `pg:"ballots" comment:"This table name comment"`
379+
Operation
380+
Ballot string `json:"ballot" comment:"This is field comment"`
381+
}
382+
383+
mockCtrl, mockSC, ctx := initMocks(t)
384+
defer mockCtrl.Finish()
385+
386+
model := Ballot{}
387+
388+
// Assert prepare
389+
tableNameCall := mockSC.
390+
EXPECT().
391+
MakeTableComment(ctx, "ballots", "This table name comment").
392+
Times(1).
393+
Return(nil)
394+
395+
createdAtCall := mockSC.
396+
EXPECT().
397+
MakeColumnComment(ctx, "ballots", "created_at", "Date of creation in seconds since UNIX epoch.").
398+
After(tableNameCall).
399+
Times(1).
400+
Return(nil)
401+
402+
updatedAtCall := mockSC.
403+
EXPECT().
404+
MakeColumnComment(ctx, "ballots", "updated_at", "Date of last update in seconds since UNIX epoch.").
405+
After(createdAtCall).
406+
Times(1).
407+
Return(nil)
408+
409+
networkCall := mockSC.
410+
EXPECT().
411+
MakeColumnComment(ctx, "ballots", "network", "Identifies belonging network.").
412+
After(updatedAtCall).
413+
Times(1).
414+
Return(nil)
415+
416+
mockSC.
417+
EXPECT().
418+
MakeColumnComment(ctx, "ballots", "ballot", "This is field comment").
419+
After(networkCall).
420+
Times(1).
421+
Return(nil)
422+
423+
// Act
424+
err := MakeComments(ctx, mockSC, model)
425+
426+
// Assert
427+
assert.Empty(t, err)
428+
}
429+
430+
func TestMakeCommentsWithStructCompositionErrorOnEmbeddedTableName(t *testing.T) {
431+
type Operation struct {
432+
//nolint
433+
tableName struct{} `pg:"operation" comment:"This embedded type tableName comment."`
434+
CreatedAt int64 `json:"-" comment:"Date of creation in seconds since UNIX epoch."`
435+
UpdatedAt int64 `json:"-" comment:"Date of last update in seconds since UNIX epoch."`
436+
Network string `json:"network" pg:",pk" comment:"Identifies belonging network."`
437+
}
438+
439+
type Ballot struct {
440+
//nolint
441+
tableName struct{} `pg:"ballots" comment:"This table name comment"`
442+
Operation
443+
Ballot string `json:"ballot" comment:"This is field comment"`
444+
}
445+
446+
mockCtrl, mockSC, ctx := initMocks(t)
447+
defer mockCtrl.Finish()
448+
449+
model := Ballot{}
450+
451+
// Assert prepare
452+
mockSC.
453+
EXPECT().
454+
MakeTableComment(ctx, "ballots", "This table name comment").
455+
Times(1).
456+
Return(nil)
457+
458+
// Act
459+
err := MakeComments(ctx, mockSC, model)
460+
461+
// Assert
462+
assert.Error(t, err, "Embedded type must not have tableName field.")
463+
}

0 commit comments

Comments
 (0)