-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(gogenerate): add optional fields in log with omitempty annotation #619
base: main
Are you sure you want to change the base?
Conversation
95f8447
to
a0135de
Compare
Signed-off-by: T M Rezoan Tamal <[email protected]>
a0135de
to
599b03a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps there should be a test for the changes you've made in processStruct
?
Also, the new log
annotation should be documented in tools/logger/generating.md
.
tools/logger/logger.go
Outdated
case strings.HasSuffix(field, tagOmitEmpty): | ||
args["key"] = strings.TrimSuffix(field, tagOmitEmpty) | ||
write(w, getSimpleOptionalFieldFormat(s.Field(kk).Type()), args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: I think the parsing needs to happen after the if field, ok := [...]
line (line 139) where you parse out the field and annotations, something like:
parts := strings.Split(",")
field = parts[0]
annotations := parts[1:] // I don't remember if this causes a panic
tools/logger/logger.go
Outdated
return fmt.Sprintf(` | ||
if s.{{.name}} != %s { | ||
addField("{{.key}}", s.{{.name}}) | ||
}`, defaultValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: perhaps this string literal should be moved to the same area as the other templated strings (at the top of the file)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did that at first, but it didn't feel very intuitive because of the %s
specifier. I will make this change, but I'm not really sure about it.
tools/logger/generating.md
Outdated
@@ -51,6 +51,41 @@ func (s *OrgInfo) MarshalLog(addField func(key string, value interface{})) { | |||
} | |||
``` | |||
|
|||
### Annotations: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: don't put the trailing colon in the header, it doesn't make sense in the autogenerated table of contents.
### Annotations: | |
### Annotations |
tools/logger/generating.md
Outdated
type OrgInfo struct | ||
Org string `log:"or.org.shortname,omitempty"` | ||
Guid *Custom `log:"or.org.guid,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: examples should follow naming conventions. In particular:
- the
Info
inOrgInfo
is superfluous Org
is duplicative of the struct nameGuid
should be in all caps since it's an acronym
Although I guess you need to fix it in the rest of the docs, too.
type OrgInfo struct | |
Org string `log:"or.org.shortname,omitempty"` | |
Guid *Custom `log:"or.org.guid,omitempty"` | |
type Org struct { | |
Shortname string `log:"or.org.shortname,omitempty"` | |
GUID *Custom `log:"or.org.guid,omitempty"` |
tools/logger/generating.md
Outdated
} | ||
``` | ||
|
||
Generated code: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: should probably note that the generated code here is post-formatted with gofmt
tools/logger/generating.md
Outdated
These tags for fields also have limited support for annotations: `ie. log:"xyz,annotation"`. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: this line is superfluous.
These tags for fields also have limited support for annotations: `ie. log:"xyz,annotation"`. |
tools/logger/generating.md
Outdated
|
||
Supported annotations: | ||
|
||
- **omitempty**: makes the field optional. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: formatting
- **omitempty**: makes the field optional. | |
- `omitempty`: makes the field optional. |
tools/logger/generating.md
Outdated
`omitempty` annotation is available for simple built-in types or | ||
custom types with underlying type being built-in or pointers of any type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: avoid repeating yourself, grammar, use common terminology (Go calls them "basic types")
`omitempty` annotation is available for simple built-in types or | |
custom types with underlying type being built-in or pointers of any type. | |
This is available for basic types, types aliased to a basic type, | |
and pointers of any type. The annotation parser does **not** validate | |
that the annotation is only applied to supported types. |
tools/logger/generating.md
Outdated
@@ -27,7 +27,7 @@ type OrgInfo struct | |||
|
|||
``` | |||
|
|||
With the addition of the annotation (which matches the [standard | |||
With the addition of the log tags `ie. log:"xyz"` for fields (xyz matches the [standard |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: wording
With the addition of the log tags `ie. log:"xyz"` for fields (xyz matches the [standard | |
With the addition of the `log` tags for fields (which matches the [standard |
tools/logger/logger.go
Outdated
@@ -168,3 +187,31 @@ func usage() { | |||
fmt.Fprintf(os.Stderr, "Flags:\n") | |||
flag.PrintDefaults() | |||
} | |||
|
|||
func getSimpleOptionalFieldFormat(p types.Type) string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: is this format "simple" if it also covers pointers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not anymore.
tools/logger/logger_test.go
Outdated
expected: true, | ||
}, | ||
{ | ||
name: "uninitialized string slice", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: wrong test name?
name: "uninitialized string slice", | |
name: "uninitialized int slice", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, copy paste.
152359d
to
b474243
Compare
@iamrz1 in the future, please avoid force pushing to PRs which have been reviewed. It makes it much more difficult to view commits which have been made since the last review. All repos in this org are set to "Squash and Merge" anyway. |
@malept Got it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems fine now, I just don't like the maintainability / readability of the ProcessStruct
table test.
return types.NewStruct(fields, tags), "BasicStruct" | ||
}, | ||
expected: ` | ||
func (s *BasicStruct) MarshalLog(addField func(key string, value interface{})) { | ||
if s == nil { | ||
return | ||
} | ||
|
||
addField("name", s.Name) | ||
addField("age", s.Age) | ||
} | ||
`, | ||
}, | ||
{ | ||
name: "time field", | ||
setup: func() (*types.Struct, string) { | ||
pkg := types.NewPackage("time", "time") | ||
timeType := types.NewNamed(types.NewTypeName(0, pkg, "Time", nil), &types.Struct{}, nil) | ||
|
||
fields := []*types.Var{ | ||
types.NewField(0, nil, "CreatedAt", timeType, false), | ||
} | ||
tags := []string{`log:"created_at"`} | ||
return types.NewStruct(fields, tags), "TimeStruct" | ||
}, | ||
expected: ` | ||
func (s *TimeStruct) MarshalLog(addField func(key string, value interface{})) { | ||
if s == nil { | ||
return | ||
} | ||
|
||
addField("created_at", s.CreatedAt.UTC().Format(time.RFC3339Nano)) | ||
} | ||
`, | ||
}, | ||
{ | ||
name: "omitempty fields", | ||
setup: func() (*types.Struct, string) { | ||
fields := []*types.Var{ | ||
types.NewField(0, nil, "Name", types.Typ[types.String], false), | ||
types.NewField(0, nil, "AgeP", types.NewPointer(types.Typ[types.Int]), false), | ||
} | ||
tags := []string{ | ||
`log:"name,omitempty"`, | ||
`log:"ageP,omitempty"`, | ||
} | ||
return types.NewStruct(fields, tags), "OmitStruct" | ||
}, | ||
expected: ` | ||
func (s *OmitStruct) MarshalLog(addField func(key string, value interface{})) { | ||
if s == nil { | ||
return | ||
} | ||
|
||
if s.Name != "" { | ||
addField("name", s.Name) | ||
} | ||
if s.AgeP != nil { | ||
addField("ageP", s.AgeP) | ||
} | ||
} | ||
`, | ||
}, | ||
{ | ||
name: "nested marshaler", | ||
setup: func() (*types.Struct, string) { | ||
nestedType := types.NewPointer(types.NewNamed( | ||
types.NewTypeName(0, nil, "NestedStruct", nil), | ||
&types.Struct{}, | ||
nil, | ||
)) | ||
fields := []*types.Var{ | ||
types.NewField(0, nil, "Nested", nestedType, false), | ||
} | ||
tags := []string{`log:"."`} | ||
return types.NewStruct(fields, tags), "ParentStruct" | ||
}, | ||
expected: ` | ||
func (s *ParentStruct) MarshalLog(addField func(key string, value interface{})) { | ||
if s == nil { | ||
return | ||
} | ||
|
||
if s.Nested != nil { | ||
s.Nested.MarshalLog(addField) | ||
} | ||
} | ||
`, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
s, name := tt.setup() | ||
buf := &bytes.Buffer{} | ||
processStruct(buf, s, name) | ||
assert.Equal(t, tt.expected, buf.String()) | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: I don't agree that this should be a table test - the setup function makes it pretty complex to read.
What this PR does / why we need it
gobox cannot be re-stenciled due to manual change in autogenerated file where a field was made optional. this PR adds
omitempty
annotation to log tags, making it possible to mark fields optional.Jira ID
DT-4572
Notes for your reviewers