-
-
Notifications
You must be signed in to change notification settings - Fork 10
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
[BACK-3177] Allow specifying custom cadence for EHR reports #769
base: master
Are you sure you want to change the base?
Conversation
d16802a
to
fe0cf08
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.
LGTM. No blockers, but I'll reiterate that I'm against taking a dependency for the duration parsing we're doing here.
ehr/reconcile/planner_test.go
Outdated
clinics = test.RandomArrayWithLength(3, clinicsTest.NewRandomClinic) | ||
tasks = make(map[string]task.Task) | ||
for _, clinic := range clinics { | ||
clinic := clinic |
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 isn't necessary here is it? I ran the test both ways and it succeeded, but sometimes these things are tricky, right?
ehr/reconcile/planner_test.go
Outdated
|
||
It("returns a task for deletion when the report settings are disabled", func() { | ||
settings := clinicsTest.NewRandomEHRSettings() | ||
settings.ScheduledReports.Cadence = api.N0d |
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.
Hah! Here's the bit from the clinic PR that I needed to know this was a disable setting! :)
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.
Changed this to DISABLED
. I'm not a big fan of the resulting code, but at least it makes it obvious to the reader when a task is disabled.
cadence := data["cadence"].(string) | ||
parsed, err := time.ParseDuration(cadence) | ||
if err != nil { | ||
return nil |
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 think it would simplify the logic if instead of returning nil
, you returned sync.DefaultCadence
. Both ScheduleNextExecution
and GetReconciliationPlan
check if the response is nil, and if so, use the default value.
To go one step further, you could instead return the error (func GetCandence(...) (time.Duration, error)
) so the caller (who presumably has more context) could decide how to handle that (whether that means using the default, or maybe logging the error before using the default) or maybe using some other value. Just returning nil (or a default) hides the error entirely. If it's really not something we care about, that's fine. If you think there's ever a time you'd want to use that error, consider returning it.
But a minor thing to be sure, and fine how it is.
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.
Both ScheduleNextExecution and GetReconciliationPlan check if the response is nil, and if so, use the default value.
This statement is not true. Planner doesn't use the default value, it check whether the cadence in the task is difference that the one in the settings, and if that's the case it updates the task:
// Update the task if the configured cadence has been changed
cadenceFromTask := sync.GetCadence(tsk.Data)
if cadenceFromTask == nil || *cadenceFromTask != cadenceFromSettings {
sync.SetCadence(tsk.Data, cadenceFromSettings)
update := task.NewTaskUpdate()
update.Data = &tsk.Data
toUpdate[tsk.ID] = update
}
This function returns exactly what the name implies. If parsing fails, then the cadence is not correctly set so it will be updated as configured. I could return the parsing error, but it will be ignored by the caller.
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 statement is not true.
You're right. I missed a case where the default might not be used.
Many minutes of reading the code later...
OK, I'm good with this now.
} | ||
|
||
func GetCadence(data map[string]interface{}) *time.Duration { | ||
cadence := data["cadence"].(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.
Uck. I get why this is here, but it's unfortunate. :( I guess someday we could update the task service now that Generics in Go are a thing.
ehr/reconcile/planner.go
Outdated
import ( | ||
"context" | ||
|
||
duration "github.com/xhit/go-str2duration/v2" |
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'm not a fan of taking a dependency for this. We support literally four values, I don't think we need a 3rd-party dependency to handle that now, or in the future.
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 removed the dependency. I don't want to have to update 2 services if we decide to support 60 day reports one day, so I had to implement the parsing logic myself. I think this results in less readable code.
clinics/service.go
Outdated
return nil, err | ||
} | ||
if response.StatusCode() != http.StatusOK || response.StatusCode() != http.StatusOK { | ||
return nil, fmt.Errorf("unexpected response status code %v from %v", response.StatusCode(), response.HTTPResponse.Request.URL) |
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.
Consider if this should use the existing platform errors code.
func (d *defaultClient) GetEHRSettings(ctx context.Context, clinicId string) (*clinic.EHRSettings, error) { | ||
response, err := d.httpClient.GetEHRSettingsWithResponse(ctx, clinicId) | ||
if err != nil { | ||
return nil, err |
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 might be a place to wrap with the existing platform error code to gain the benefits it brings.
clinicId := *clinic.Id | ||
settings, err := p.clinicsClient.GetEHRSettings(ctx, clinicId) | ||
if err != nil { | ||
return nil, err |
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.
The clinicsClient
appears to be generated code that doesn't return platform errors. Therefore you might consider if these should use errors.Wrap
. (I won't mention this any more for this PR, but there are other places I haven't pointed out.)
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.
Can you please clarify what are the benefits of using the platform errors.Wrap
, because I'm not familiar with those.
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.
@darinkrauss is for sure the expert on that. But I believe he'd say they bring a consistent format to errors, and sometimes they add line numbers for finding the source of the error.
I thought the errors package was still in use for new code in platform, no? If not, I'll make a note and try to stop using/suggesting 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.
I've been using the standard library most recently. This may be incorrect, but it's not obvious or documented why the platform errors package should be used instead, hence my question.
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.
Hmm, what do folks think about me writing a "Guide to Writing Consistent Code in the Platform Repo"? 😉
Yes, there is a reason to use the Platform errors
package and, more explicitly, the errors.Wrap
functionality. The errors
package adds a lot more context to the error that chains correctly with multiple wraps. Now, while the current Golang errors
package does some of that, the rest of Platform isn't built to handle that in any way.
So, in short, if you want errors to propagate correctly and do the "right thing" in the rest of Platform, then use the Platform errors
package and use errors.Wrap
when you want to add further context or explanation to the error.
I'm fine if we want to intentionally revisit this and make appropriate changes, but IMO, we should stick with how it is currently done so it works and is written as expected.
My 2 cents...
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.
Thanks for the explanation - I'll make the change, because those errors might eventually bubble up to the task error handler.
d725e3e
to
7f67c05
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.
Thank you for pointing out my misunderstanding. Still no blockers IMO. See @darinkrauss re platform/errors usage, if my explanation isn't satisfactory.
clinicId := *clinic.Id | ||
settings, err := p.clinicsClient.GetEHRSettings(ctx, clinicId) | ||
if err != nil { | ||
return nil, err |
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.
@darinkrauss is for sure the expert on that. But I believe he'd say they bring a consistent format to errors, and sometimes they add line numbers for finding the source of the error.
I thought the errors package was still in use for new code in platform, no? If not, I'll make a note and try to stop using/suggesting it.
cadence := data["cadence"].(string) | ||
parsed, err := time.ParseDuration(cadence) | ||
if err != nil { | ||
return nil |
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 statement is not true.
You're right. I missed a case where the default might not be used.
Many minutes of reading the code later...
OK, I'm good with this now.
Oh, looks like there's a go import re-ordering that needs to be fixed to make the linter happy. https://app.travis-ci.com/github/tidepool-org/platform/builds/272549249#L442 |
7f67c05
to
5100baa
Compare
a311e45
to
2b21ec7
Compare
a3ee552
to
4404cc9
Compare
No description provided.