-
Notifications
You must be signed in to change notification settings - Fork 3
Story/vspc 204 #308
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
base: develop
Are you sure you want to change the base?
Story/vspc 204 #308
Conversation
|
Can one of the admins verify this patch? |
jdamerow
left a comment
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 didn't not go through everything but given I'm asking for some major changes, I'll leave it with this for now.
| }); | ||
| IOUtils.close(responseZipStream); | ||
| IOUtils.close(bufferedOutputStream); | ||
| IOUtils.close(byteArrayOutputStream); |
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 can probably be tightened up by using try-with statements
| responseZipStream.closeEntry(); | ||
|
|
||
| } catch (IOException e) { | ||
| System.err.println(e); |
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.
?
| return byteArrayOutputStream.toByteArray(); | ||
|
|
||
| } catch (IOException e) { | ||
| throw new IOException(e); |
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.
there is no point in wrapping an IOException in another IOException
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 still
vspace/src/main/java/edu/asu/diging/vspace/core/file/impl/StorageEngine.java
Show resolved
Hide resolved
|
|
||
|
|
||
| @Service | ||
| public class DownloadsManager { |
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.
needs an interface
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.
From what I can tell so far, this class needs to be a little refactored. There should be several methods:
- create a snapshot (or whatever you want to call the generated html pages)
- get one
- list all
- get generated files
When the user generates a new snapshot the create method is called. This should be an @Async method as it might take a while. The user sees a progress page (polling for updates from the front end, not just one call as the server might time out).
When the generation process is done, the user will be sent to the page for that snapshot. The create method should return the created ExhibitionDownload object. The get method is probably called here, to get the object by its id. If the user wants to download the files, they can do so from that page (get generated files method will be called).
The list all snapshots page will call the list method.
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.
@jdamerow , want to check my understanding on this once.
The first time user clicks on download button, the async rest api will return an newly created exhibitionDownload object with a unique folder name (used timestamps). this api will trigger create snapshot method.
then Ui will poll another rest api (this will be an simple GET api) by sending exhibitionDownload object as input, untill the requested folder is ready. once its ready, the folder should be download as zip folder.
so there will be 2 different rest apis in backend, one async which just returns exhibitionDownload object and trigger createSnapshot method. another one will be used simply to poll.
is this understanding correct?
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.
Not 100% sure. What do you mean by "async rest api"?
The flow would be:
- POST request to start zip generation (the request itself does not have to be async, but it will start an async method on the server)
- the controller will return an id that the client can then use to poll (id could be the id of an exhibitionDownload object if that is being created and returned by the async method)
- "then Ui will poll another rest api (this will be an simple GET api)" - yes, "by sending exhibitionDownload object as input" -> not sure what that means. It would use the id returned in the previous step
- "untill the requested folder is ready." - yes
- "once its ready, the folder should be download as zip folder." - once it's ready the user should be able to down load, it should probably show a message that the generation is done or something. It does not have to automatically start downloading.
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.
@jdamerow , to show the progress of download to the user, do we want to create a separate page where user will be redirected to, or can we show it within a section in the same downloadlist page?
I tried putting it in an alert box, but if the page gets refreshed, the user won't be able to see the notification.
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.
either is fine.
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.
if it's on the same page and the user won't see the progress anymore after reloading the page, there is needs to be another indication that makes clear that export is in progress.
vspace/src/main/java/edu/asu/diging/vspace/core/file/impl/StorageEngine.java
Outdated
Show resolved
Hide resolved
| }); | ||
| IOUtils.close(responseZipStream); | ||
| IOUtils.close(bufferedOutputStream); | ||
| IOUtils.close(byteArrayOutputStream); |
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.
are these needed since you're using the try-with statement?
vspace/src/main/java/edu/asu/diging/vspace/core/file/impl/StorageEngine.java
Show resolved
Hide resolved
vspace/src/main/java/edu/asu/diging/vspace/core/model/IExhibitionDownload.java
Outdated
Show resolved
Hide resolved
| Resource resource = null; | ||
| try { | ||
| String pathToResources = request.getServletContext().getRealPath("") + "/resources"; | ||
| String exhibitionFolderName= "Exhibition"+ LocalDateTime.now(); |
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.
business logic that should not be in the controller
# Conflicts: # vspace/src/test/java/edu/asu/diging/vspace/core/services/impl/ImageServiceTest.java
|
Make it so, Jenkins. |
|
A couple of issues:
|
vspace/pom.xml
Outdated
| <uploaded.files.path></uploaded.files.path> | ||
|
|
||
| <downloaded.files.path></downloaded.files.path> | ||
| <resources.path></resources.path> |
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.
these two need a comment what should go here.
| */ | ||
| @Async | ||
| @Transactional | ||
| public Future<SnapshotTask> createSnapshot(String resourcesPath, String exhibitionFolderName,SequenceHistory sequenceHistory, ExhibitionSnapshot exhibitionSnapshot) |
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.
ideally, resource path would not be required but calculate.
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 still
| import edu.asu.diging.vspace.core.services.IRenderingManager; | ||
|
|
||
| @Service | ||
| public class AsyncSnapshotCreator { |
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.
needs an interface
| htmlContent.append(" </div>\n"); | ||
| htmlContent.append(" </div>\n"); | ||
| htmlContent.append("</body>\n"); | ||
| htmlContent.append("</html>"); |
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 should be done using a template like the rest of the files.
| */ | ||
| @Async | ||
| @Transactional | ||
| public Future<SnapshotTask> createSnapshot(String resourcesPath, String exhibitionFolderName,SequenceHistory sequenceHistory, ExhibitionSnapshot exhibitionSnapshot) |
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 still
| Set<String> visitedSequences = new HashSet<String>(); | ||
| createSequencesSnapshot(startSequence, module, space, spaceFolderName, imagesFolder, visitedSequences); | ||
| } catch (FileStorageException e) { | ||
| logger.error("Could not download Module",e); |
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.
error like this need to be communicated to the user; they need to know if a snapshot couldn't fully be created.
| * @param context | ||
| * @param id | ||
| */ | ||
| private void populateContextForSpace(Context context, String id, SequenceHistory sequenceHistory) { |
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.
it seems like sequenceHistory is never used?
| exhibitionSnapshot = exhibitionSnapshotRepository.save(exhibitionSnapshot); | ||
|
|
||
| try { | ||
| snapshotTask = createSnapshot(resourcesPath, exhibitionFolderName, sequenceHistory, exhibitionSnapshot); |
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 doesn't seem to make sense; if sequenceHistory is autowired, wouldn't it get the history of the current user? that should not be used to generate a snapshot. I don't think sequence history is at important for snapshots?
vspace/src/main/java/edu/asu/diging/vspace/core/services/impl/SnapshotManager.java
Show resolved
Hide resolved
… indexDownloadTemplate html file for dynamic downloads
…d code style, updated interface
|
|
||
| <uploaded.files.path>${project.basedir}/uploads</uploaded.files.path> | ||
| <downloaded.files.path>${project.basedir}/downloads</downloaded.files.path> | ||
| <resources.path>${project.basedir}/src/main/webapp/resources</resources.path> |
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 believe project.basedir is an absolute path to the base directory of the project? This does not work, as the war will be build on a different system than where it is deployed, so the directory will be a different one. where ever those variables (uploaded.files.path, etc) are being used, it should use relative paths for directories that are within the project, and absolute paths set through configurations for folders outside of the project..
| byte[] bytes = byteOutput.toByteArray(); | ||
| byteOutput.close(); | ||
| return bytes; | ||
| catch(IOException e) { |
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.
should be on line 106 according to our style guide
| throw new IOException(e.getMessage(), e); | ||
| } | ||
| } | ||
| // Delete the folder |
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.
useless comment; comments should explain why not what (unless it's not obvious from the code, which it is here).
| // Delete the folder | ||
| deleteFolder(folderPath, folderName); | ||
| } catch (IOException e) { | ||
| // Delete the zip file if an exception occurred |
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.
same here
| // Get exhibition details | ||
| IExhibition exhibition = exhibitionManager.getStartExhibition(); | ||
| String exhibitionTitle = exhibition != null ? exhibition.getTitle() : "Virtual Exhibition"; | ||
| Space startSpace = exhibition != null && exhibition.getStartSpace() != null ? |
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.
Why not use the interface here instead of Space?
| try { | ||
| return storageEngineDownloads.getMediaContent("", exhibitionSnapshot.get().getFolderName() + ZIP_FILE_EXTENSION); | ||
| } catch (IOException e) { | ||
| throw new ExhibitionSnapshotNotFoundException(e.getMessage(), e); |
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.
if you just throw the exception, there is no reason to repeat the message of the wrapped exception. Either have a message that explains something or just not provide a message at all (and just wrap the exception).
|
|
||
| Pageable requestedPageForFiles = PageRequest.of(filesPagenum - 1, pageSize); | ||
| Page<ExhibitionSnapshot> page = exhibitionSnapshotRepository.findAllByOrderByCreationDateDesc(requestedPageForFiles); | ||
| return page.map(exhibitionSnapshot-> {return (ExhibitionSnapshot) exhibitionSnapshot;}); |
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 confused, what is this line doing? looks like it's mapping snapshots onto themselves?
| if(snapshotTask.isPresent()) { | ||
| return snapshotTask.get(); | ||
| } | ||
| else { |
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.
code style
| $('.successAlert').append(alert); | ||
| const poll = (id, folderName) => { | ||
| maxAttempts = 10; | ||
| interval= 3000; |
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.
is that miliseconds? (needs a comment)
| var alert = $('<div class="alert alert-info alert-dismissible fade show" role="alert"><p>Snapshot '+folderName+' creation is in progress. Please wait. </p><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button></div>'); | ||
| $('.successAlert').append(alert); | ||
| const poll = (id, folderName) => { | ||
| maxAttempts = 10; |
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 odd. Why would polling for a completed task have a maximum number of attempts. It should poll until complete or there was an error.
… paths, added comments, removed useless comments, fixed error messages, added ISpace iterface in AysncSnapShotCreator
…nd development, fixed UI issue
Guidelines for Pull Requests
If you haven't yet read our code review guidelines, please do so, You can find them here.
Please confirm the following by adding an x for each item (turn
[ ]into[x]).Please provide a brief description of your ticket
The user would get a zip file that contains one html page for each page + any css, javascript, etc.
Anything else the reviewer needs to know?
... describe here ...