Conversation
jrschumacher
left a comment
There was a problem hiding this comment.
@Fesaa There might be some additional checks you want to make, but I think this is a good start. The validator is good, but its a bit confusing if you don't know Go since it will format the errors like the struct not the yaml.
| slog.Error("Error loading config") | ||
| for _, l := range strings.Split(e.Error(), "\n") { | ||
| slog.Error(l) | ||
| } |
There was a problem hiding this comment.
This will give error messages like:
2025/01/05 10:07:38 ERROR Error loading config
2025/01/05 10:07:38 ERROR config validation errors:
2025/01/05 10:07:38 ERROR Config.Sources[1].Heartbeat is required
2025/01/05 10:07:38 ERROR Config.Sources[1].Name is required
2025/01/05 10:07:38 ERROR Config.Sources[1].Info is required
panic: config validation errors:
Config.Sources[1].Heartbeat is required
Config.Sources[1].Name is required
Config.Sources[1].Info is required
goroutine 1 [running]:
main.main()
/Users/ryan/Projects/iCalMerger/main.go:58 +0x4d4
exit status 2There was a problem hiding this comment.
I'm not sure, I like the weird splitting on new line here.
After you've made the changes to []error slice, and using errors.Join. Can you show me what slog.Error("Error loading config", "error", e) looks like?
If it looks better by calling slog on each error, we should return a slice of errors instead of splitting on newline
| errs := make([]string, len(verr)) | ||
| for i, e := range verr { | ||
| if e.StructNamespace() == "Config.Sources" { | ||
| if e.Tag() == "unique" { | ||
| errs[i] = fmt.Sprintf("%s.%s is not unique", e.Namespace(), e.Param()) | ||
| } | ||
| } else { | ||
| errs[i] = fmt.Sprintf("%s is %s", e.Namespace(), e.Tag()) | ||
| } | ||
| } | ||
| return nil, fmt.Errorf("config validation errors:\n%v", strings.Join(errs, "\n")) |
There was a problem hiding this comment.
This is required since the unique=EndPoint tag will not return a good error unlike the other validation tags. There is still no way to denote the location of the infringing config line (i.e. Sources[1].EndPoint) so the best we can do is
2025/01/05 10:00:41 ERROR Error loading config
2025/01/05 10:00:41 ERROR validation error: Config.Sources.EndPoint is not unique
panic: validation error: Config.Sources.EndPoint is not unique
goroutine 1 [running]:
main.main()
/Users/ryan/Projects/iCalMerger/main.go:58 +0x4d4
exit status 2| Name string `yaml:"name,omitempty"` | ||
| Component string `yaml:"component,omitempty"` | ||
| Check string `yaml:"check"` | ||
| Check string `yaml:"check" validate:"required"` |
There was a problem hiding this comment.
@Fesaa I'm not sure how the FirstOfYear, FirstOfMonth, and FirstOfDay work exactly. If Rules and modifiers can both use them then we can add a oneof.
There was a problem hiding this comment.
Yes, the filters inside a modifier can use all the rules avaible.
|
|
||
| type Rule struct { | ||
| Name string `yaml:"name,omitempty"` | ||
| Component string `yaml:"component,omitempty"` |
There was a problem hiding this comment.
@Fesaa can this really be empty? All the checks are expecting the component to be defined https://github.com/Fesaa/iCalMerger/blob/master/ical/checks.go#L71
There was a problem hiding this comment.
The firstOfX rules do not need a name, or component. They can stay as omitempty
|
To keep you in the loop, my exams have/are starting so it may be a while before I can properly review this. But from a glance it looks fine, but I haven't had the chance to pull it down and test yet. |
|
No worries. Just wanted to follow up on your request. Best of luck on your exams @Fesaa! |
Fesaa
left a comment
There was a problem hiding this comment.
Amazing work, thanks!
Left some comments here and there that will need to be resolved before merging. I will look into the TODO's you've left in the tests (firstOf) after my exams and resolve them, thanks. Thanks for bringing these up, looks like test are amazing!
|
|
||
| type Rule struct { | ||
| Name string `yaml:"name,omitempty"` | ||
| Component string `yaml:"component,omitempty"` |
There was a problem hiding this comment.
The firstOfX rules do not need a name, or component. They can stay as omitempty
| Name string `yaml:"name,omitempty"` | ||
| Component string `yaml:"component,omitempty"` | ||
| Check string `yaml:"check"` | ||
| Check string `yaml:"check" validate:"required"` |
There was a problem hiding this comment.
Yes, the filters inside a modifier can use all the rules avaible.
| } | ||
| validate := validator.New(validator.WithRequiredStructEnabled()) | ||
| if err := validate.Struct(config); err != nil { | ||
| verr := err.(validator.ValidationErrors) |
There was a problem hiding this comment.
Type assertion on errors fails on wrapped errors - my IDE
I don't know if it could ever return a wrapped error, but worth following.
| errs := make([]string, len(verr)) | ||
| for i, e := range verr { | ||
| if e.StructNamespace() == "Config.Sources" { | ||
| if e.Tag() == "unique" { | ||
| errs[i] = fmt.Sprintf("%s.%s is not unique", e.Namespace(), e.Param()) | ||
| } | ||
| } else { | ||
| errs[i] = fmt.Sprintf("%s is %s", e.Namespace(), e.Tag()) | ||
| } | ||
| } | ||
| return nil, fmt.Errorf("config validation errors:\n%v", strings.Join(errs, "\n")) |
| errs[i] = fmt.Sprintf("%s is %s", e.Namespace(), e.Tag()) | ||
| } | ||
| } | ||
| return nil, fmt.Errorf("config validation errors:\n%v", strings.Join(errs, "\n")) |
There was a problem hiding this comment.
Please use errors.Join instead of strings.Join.
And make errs a slice of errors, and then fmt.Errorf. Thanks
| } | ||
|
|
||
| err := cfg.Validate() | ||
| validate := validator.New() |
There was a problem hiding this comment.
Use the same options (validator.WithRequiredStructEnabled()) as in the software
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func TestLoadConfig(t *testing.T) { |
There was a problem hiding this comment.
testing.T provides a t.TempDir() method, I would opt to use this as we're in a testing env.
| }, | ||
| { | ||
| EndPoint: "http://example.com/endpoint2", | ||
| EndPoint: "/endpoint2", |
There was a problem hiding this comment.
Does the test fail without these changes? If so, great! If not, we'll have to make sure it does. And add a test for it.
Or change the code to allow it. But I believe it would break at this moment.
| } | ||
|
|
||
| err := info.Validate() | ||
| validate := validator.New() |
There was a problem hiding this comment.
Same as above, make sure to use the same options for the validator (validator.WithRequiredStructEnabled())
| slog.Error("Error loading config") | ||
| for _, l := range strings.Split(e.Error(), "\n") { | ||
| slog.Error(l) | ||
| } |
There was a problem hiding this comment.
I'm not sure, I like the weird splitting on new line here.
After you've made the changes to []error slice, and using errors.Join. Can you show me what slog.Error("Error loading config", "error", e) looks like?
If it looks better by calling slog on each error, we should return a slice of errors instead of splitting on newline
Closes #5