onegov-cloud
onegov-cloud copied to clipboard
Delete organigram in model before exchanging
Agency: Fixes required forced browser refresh (Ctrl+F5) after organigram replacement
TYPE: Bugfix LINK: ogc-1346
Codecov Report
Attention: 1 lines in your changes are missing coverage. Please review.
Comparison is base (
a914aa7) 87.64% compared to head (8135f42) 87.63%. Report is 23 commits behind head on master.
| Files | Patch % | Lines |
|---|---|---|
| src/onegov/file/integration.py | 93.33% | 1 Missing :warning: |
:exclamation: Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@ Coverage Diff @@
## master #1090 +/- ##
==========================================
- Coverage 87.64% 87.63% -0.02%
==========================================
Files 1185 1185
Lines 77924 77939 +15
==========================================
Hits 68300 68300
- Misses 9624 9639 +15
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
@Daverball I found when exchanging the organigram attachement I need to delete the model.organigram to ensure a new storage id will be created with the new attachement and therefore a browser refresh is not needed.
Is this the right approach?
You can probably use Cache-Control headers instead, we use them heavily in ElectionDay views, since those are cached. You may need to add a separate view for the HEAD request which only sets the header.
Not sure if disabling the cache globally is the best idea, I was more thinking of setting headers in individual views. Although if it's just the standard file view (i.e. the organigram doesn't use its own view) the flaw might be in onegov.file we should probably send the timestamp of the last modification on the file so the browser can decide whether the cache needs to be invalidated or not.
I found Events do not have the issue when replacing the image. So l looked into it. The difference is form.populate_obj() vs form.update_model(). I will investigate on this...
populate_obj is just the regular wtforms method that does a setattr on the object for each field in the form, update_model is a convention we sometimes use when the form doesn't map as cleanly and we have to write most of the attribute setting ourselves anyways, so that shouldn't really matter, from what I can tell the organigram is a regular file, so the issue is just that we don't send a last modified header, so the browser has no idea when the file is out of date and needs to be refreshed, hence the need for manually refreshing.
You can delete the file and generate a new one so the link changes, but setting the last modified header seems like a more permanent solution, since this affects all uploaded files and not just this one. See here for how we set the last modified header in election day: https://github.com/OneGov/onegov-cloud/blob/1fb939568d35c6a4fd2619b4de9e123c5319c1e8/src/onegov/election_day/utils/common.py#L59-L69
You should be able to retrieve the last modified from either the file reference, or you can use the timestamp on the file model which should also change when the reference changes. The file view is here: https://github.com/OneGov/onegov-cloud/blob/1fb939568d35c6a4fd2619b4de9e123c5319c1e8/src/onegov/file/integration.py#L486-L490
It might be worth adding a HEAD view for files that just sets the headers. Here's an example of a head view in election day: https://github.com/OneGov/onegov-cloud/blob/1fb939568d35c6a4fd2619b4de9e123c5319c1e8/src/onegov/election_day/views/election/main.py#L28-L42
Unfortunately the 'last-modified' response header did not resolve the issue although it was set correctly. A regular refresh did not reload the image resource, see last commit.
The root cause, in my opinion, is the resource (organigram) does not get a new id when exchanging the image. if i delete first, then add a new organigram I get a new resource id hence a proper page reload.
I suggest to go back to my original attempt which ends up in a new resource id upon exchanging the organigram. What do you think @Daverball?
You need to set it in the file view not the view that links to the file, the file is a separate request, setting it on the html does absolutely nothing, since that isn't cached to begin with (unless you manually enable it through cache control headers), that's why i pointed you towards the file view and not the agency view.
This is also why I said it affects all files and not just the ones in agency. So this issue will crop up again and again unless we fix it correctly.
After moving the add_last_modified_headers to the 'file view head' and some debugging I see that view_file is only called on browser 'force refresh' but not on 'refresh'. On the other hand view_file_head doesn't get called in either case. I might still doing wrong or the approach does not work out.
I also found that the def organigram_file setter method handles two cases, one to replace one for a new organigram file.
A very similar use case to this can be found in the Agency model. There we have a pdf_file setter method doing the same with a pdf file. Instead it always does a new pdf, hence a new id. So I tried the same in organigram_file (always doing the 'else' path) and it worked.
As you said the downside is that it only solved the issue for agency...
@Tschuppi81 It might have something to do with that we're also setting max age on the Cache-Control header. Caching behavior is weird and complicated. Do you still not see any HEAD requests if you remove the max age? I think we're throttling the HEAD requests on files to one every minute, so it would take at least one minute to see changes reflected on files.
It's also possible that bjoern does some file caching of its own, to speed up file requests, of which there are usually many. In which case I guess we can rely on the workaround.
Also you might need to add the last modified header to be present in the file view as well, so the browser has a frame of reference.
Since the head view calls the file view, you should just be able to move the function that sets the last modified header to the file view.
No, I still don't see any head views calls when removing max age...
I also did various changes regarding caching, changing the headers. Nothing helped there as the file view does not get called for after editing the organigram of an Agency. In contrast changing the image of an Event always invokes the file view leading to a proper refreshed view.
So I started to investigate on the different behavior and found the Agency model changes the reference under the hood and the organigram does not get to know it. So it is better to set a new organigram like show below.
After changing the Agency model, organigram_file setter from
if self.organigram:
self.organigram.reference = as_fileintent(value, filename)
...
else:
organigram = AgencyOrganigram(id=random_token())
...
to
organigram = AgencyOrganigram(id=random_token())
organigram.reference = as_fileintent(value, filename)
organigram.name = filename
self.organigram = organigram
it starts working, independent of caching changes.
I'd like to investigate this myself at some point, but I guess there's also the issue of File deriving a whole bunch of meta information from StoredFile when it is first created. I forget if associated with a one-to-one cascades the delete, I think it does, but it's probably a good idea to verify in a test that the old file gets deleted.
I don't mind changing the behavior to replace the file entirely in this specific case, although I'd do a git blame on it to investigate whether this branch has been added intentionally to fix another bug, or if maybe the branch used to be there in the other model and was removed for the same reason.