Merge branch 'DS-3580_workspaceitem' of https://github.com/4Science/DSpace into inputform-rows

This commit is contained in:
Andrea Bollini
2018-10-10 11:45:02 +02:00
8 changed files with 89 additions and 46 deletions

View File

@@ -444,8 +444,6 @@ public class RestResourceController implements InitializingBean {
* the rest model that identify the REST resource collection * the rest model that identify the REST resource collection
* @param id * @param id
* the id of the specific rest resource * the id of the specific rest resource
* @param extraField
* the original name of the uploaded file
* @param uploadfile * @param uploadfile
* the file to upload * the file to upload
* @return the created resource * @return the created resource
@@ -457,12 +455,10 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory, @PathVariable String apiCategory,
@PathVariable String model, @PathVariable String model,
@PathVariable Integer id, @PathVariable Integer id,
@RequestParam(required = false, value =
"extraField") String extraField,
@RequestParam("file") MultipartFile @RequestParam("file") MultipartFile
uploadfile) uploadfile)
throws HttpRequestMethodNotSupportedException { throws HttpRequestMethodNotSupportedException {
return uploadInternal(request, apiCategory, model, id, extraField, uploadfile); return uploadInternal(request, apiCategory, model, id, uploadfile);
} }
/** /**
@@ -478,8 +474,6 @@ public class RestResourceController implements InitializingBean {
* the rest model that identify the REST resource collection * the rest model that identify the REST resource collection
* @param id * @param id
* the id of the specific rest resource * the id of the specific rest resource
* @param extraField
* the original name of the uploaded file
* @param uploadfile * @param uploadfile
* the file to upload * the file to upload
* @return the created resource * @return the created resource
@@ -491,12 +485,10 @@ public class RestResourceController implements InitializingBean {
@PathVariable String apiCategory, @PathVariable String apiCategory,
@PathVariable String model, @PathVariable String model,
@PathVariable UUID id, @PathVariable UUID id,
@RequestParam(required = false, value =
"extraField") String extraField,
@RequestParam("file") MultipartFile @RequestParam("file") MultipartFile
uploadfile) uploadfile)
throws HttpRequestMethodNotSupportedException { throws HttpRequestMethodNotSupportedException {
return uploadInternal(request, apiCategory, model, id, extraField, uploadfile); return uploadInternal(request, apiCategory, model, id, uploadfile);
} }
/** /**
@@ -506,21 +498,19 @@ public class RestResourceController implements InitializingBean {
* @param apiCategory * @param apiCategory
* @param model * @param model
* @param id * @param id
* @param extraField
* @param uploadfile * @param uploadfile
* @return * @return
*/ */
private <ID extends Serializable> ResponseEntity<ResourceSupport> uploadInternal(HttpServletRequest request, private <ID extends Serializable> ResponseEntity<ResourceSupport> uploadInternal(HttpServletRequest request,
String apiCategory, String model, String apiCategory, String model,
ID id, ID id,
String extraField,
MultipartFile uploadfile) { MultipartFile uploadfile) {
checkModelPluralForm(apiCategory, model); checkModelPluralForm(apiCategory, model);
DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model); DSpaceRestRepository<RestAddressableModel, ID> repository = utils.getResourceRepository(apiCategory, model);
RestAddressableModel modelObject = null; RestAddressableModel modelObject = null;
try { try {
modelObject = repository.upload(request, apiCategory, model, id, extraField, uploadfile); modelObject = repository.upload(request, apiCategory, model, id, uploadfile);
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
return ControllerUtils.toEmptyResponse(HttpStatus.INTERNAL_SERVER_ERROR); return ControllerUtils.toEmptyResponse(HttpStatus.INTERNAL_SERVER_ERROR);
@@ -552,8 +542,6 @@ public class RestResourceController implements InitializingBean {
public <T extends RestAddressableModel> ResponseEntity<ResourceSupport> upload(HttpServletRequest request, public <T extends RestAddressableModel> ResponseEntity<ResourceSupport> upload(HttpServletRequest request,
@PathVariable String apiCategory, @PathVariable String apiCategory,
@PathVariable String model, @PathVariable String model,
@RequestParam(required = false)
String extraField,
@RequestParam("file") MultipartFile @RequestParam("file") MultipartFile
uploadfile) uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException { throws SQLException, FileNotFoundException, IOException, AuthorizeException {
@@ -561,7 +549,7 @@ public class RestResourceController implements InitializingBean {
checkModelPluralForm(apiCategory, model); checkModelPluralForm(apiCategory, model);
DSpaceRestRepository repository = utils.getResourceRepository(apiCategory, model); DSpaceRestRepository repository = utils.getResourceRepository(apiCategory, model);
Iterable<T> content = repository.upload(request, extraField, uploadfile); Iterable<T> content = repository.upload(request, uploadfile);
List<DSpaceResource> resources = new ArrayList<>(); List<DSpaceResource> resources = new ArrayList<>();
for (T modelObject : content) { for (T modelObject : content) {

View File

@@ -298,15 +298,13 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
* @param model * @param model
* @param id * @param id
* the ID of the target REST object * the ID of the target REST object
* @param extraField
* the original name of the uploaded file
* @param file * @param file
* the uploaded file * the uploaded file
* @return the new state of the REST object * @return the new state of the REST object
* @throws Exception * @throws Exception
*/ */
public T upload(HttpServletRequest request, String apiCategory, String model, public T upload(HttpServletRequest request, String apiCategory, String model,
ID id, String extraField, MultipartFile file) throws Exception { ID id, MultipartFile file) throws Exception {
throw new RuntimeException("No implementation found; Method not allowed!"); throw new RuntimeException("No implementation found; Method not allowed!");
} }
@@ -373,8 +371,6 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
* *
* @param request * @param request
* the http request * the http request
* @param extraField
* the original name of the uploaded file
* @param uploadfile * @param uploadfile
* the file to process * the file to process
* @return the created objects * @return the created objects
@@ -383,10 +379,10 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
* @throws IOException * @throws IOException
* @throws AuthorizeException * @throws AuthorizeException
*/ */
public Iterable<T> upload(HttpServletRequest request, String extraField, MultipartFile uploadfile) public Iterable<T> upload(HttpServletRequest request, MultipartFile uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException { throws SQLException, FileNotFoundException, IOException, AuthorizeException {
Context context = obtainContext(); Context context = obtainContext();
Iterable<T> entity = upload(context, request, extraField, uploadfile); Iterable<T> entity = upload(context, request, uploadfile);
context.commit(); context.commit();
return entity; return entity;
} }
@@ -396,8 +392,6 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
* *
* @param request * @param request
* the http request * the http request
* @param extraField
* the original name of the uploaded file
* @param uploadfile * @param uploadfile
* the file to process * the file to process
* @return the created objects * @return the created objects
@@ -407,7 +401,7 @@ public abstract class DSpaceRestRepository<T extends RestAddressableModel, ID ex
* @throws AuthorizeException * @throws AuthorizeException
* @throws RepositoryMethodNotImplementedException * @throws RepositoryMethodNotImplementedException
*/ */
protected Iterable<T> upload(Context context, HttpServletRequest request, String extraField, protected Iterable<T> upload(Context context, HttpServletRequest request,
MultipartFile uploadfile) MultipartFile uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException { throws SQLException, FileNotFoundException, IOException, AuthorizeException {
throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", ""); throw new RepositoryMethodNotImplementedException("No implementation found; Method not allowed!", "");

View File

@@ -219,7 +219,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
//TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')") //TODO @PreAuthorize("hasPermission(#id, 'WORKSPACEITEM', 'WRITE')")
@Override @Override
public WorkspaceItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id, public WorkspaceItemRest upload(HttpServletRequest request, String apiCategory, String model, Integer id,
String extraField, MultipartFile file) throws Exception { MultipartFile file) throws Exception {
Context context = obtainContext(); Context context = obtainContext();
WorkspaceItemRest wsi = findOne(id); WorkspaceItemRest wsi = findOne(id);
@@ -244,7 +244,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
UploadableStep uploadableStep = (UploadableStep) stepInstance; UploadableStep uploadableStep = (UploadableStep) stepInstance;
uploadableStep.doPreProcessing(context, source); uploadableStep.doPreProcessing(context, source);
ErrorRest err = ErrorRest err =
uploadableStep.upload(context, submissionService, stepConfig, source, file, extraField); uploadableStep.upload(context, submissionService, stepConfig, source, file);
uploadableStep.doPostProcessing(context, source); uploadableStep.doPostProcessing(context, source);
if (err != null) { if (err != null) {
errors.add(err); errors.add(err);
@@ -347,7 +347,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
} }
@Override @Override
public Iterable<WorkspaceItemRest> upload(Context context, HttpServletRequest request, String extraField, public Iterable<WorkspaceItemRest> upload(Context context, HttpServletRequest request,
MultipartFile uploadfile) MultipartFile uploadfile)
throws SQLException, FileNotFoundException, IOException, AuthorizeException { throws SQLException, FileNotFoundException, IOException, AuthorizeException {
File file = Utils.getFile(uploadfile, "upload-loader", "filedataloader"); File file = Utils.getFile(uploadfile, "upload-loader", "filedataloader");
@@ -467,7 +467,7 @@ public class WorkspaceItemRestRepository extends DSpaceRestRepository<WorkspaceI
if (UploadableStep.class.isAssignableFrom(stepClass)) { if (UploadableStep.class.isAssignableFrom(stepClass)) {
UploadableStep uploadableStep = (UploadableStep) stepInstance; UploadableStep uploadableStep = (UploadableStep) stepInstance;
ErrorRest err = uploadableStep.upload(context, submissionService, stepConfig, wi, ErrorRest err = uploadableStep.upload(context, submissionService, stepConfig, wi,
uploadfile, extraField); uploadfile);
if (err != null) { if (err != null) {
errors.add(err); errors.add(err);
} }

View File

@@ -23,7 +23,24 @@ import org.springframework.web.multipart.MultipartFile;
*/ */
public interface UploadableStep extends ListenerProcessingStep { public interface UploadableStep extends ListenerProcessingStep {
/**
* The method to implement to support upload of a file in the submission section (aka panel / step)
*
* @param context
* the dspace context
* @param submissionService
* the submission service
* @param stepConfig
* the configuration of the submission section
* @param wsi
* the inprogress submission
* @param file
* the multipart file, please note that it is a complex object containing additional information other
* than just the binary such as the filename and the mimetype
* @return the encountered error if any
* @throws IOException
*/
public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig, public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig,
InProgressSubmission wsi, MultipartFile file, String extraField) throws IOException; InProgressSubmission wsi, MultipartFile file) throws IOException;
} }

View File

@@ -47,7 +47,7 @@ public class ExtractMetadataStep extends ExtractionStep implements UploadableSte
@Override @Override
public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig, public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig,
InProgressSubmission wsi, MultipartFile multipartFile, String extraField) InProgressSubmission wsi, MultipartFile multipartFile)
throws IOException { throws IOException {
Item item = wsi.getItem(); Item item = wsi.getItem();
@@ -66,7 +66,7 @@ public class ExtractMetadataStep extends ExtractionStep implements UploadableSte
} }
FileDataLoader fdl = (FileDataLoader) dataLoader; FileDataLoader fdl = (FileDataLoader) dataLoader;
fdl.setFilename(file.getAbsolutePath()); fdl.setFilename(Utils.getFileName(multipartFile));
recordSet = convertFields(dataLoader.getRecords(), bteBatchImportService.getOutputMap()); recordSet = convertFields(dataLoader.getRecords(), bteBatchImportService.getOutputMap());

View File

@@ -22,6 +22,7 @@ import org.dspace.app.rest.submit.SubmissionService;
import org.dspace.app.rest.submit.UploadableStep; import org.dspace.app.rest.submit.UploadableStep;
import org.dspace.app.rest.submit.factory.PatchOperationFactory; import org.dspace.app.rest.submit.factory.PatchOperationFactory;
import org.dspace.app.rest.submit.factory.impl.PatchOperation; import org.dspace.app.rest.submit.factory.impl.PatchOperation;
import org.dspace.app.rest.utils.Utils;
import org.dspace.app.util.SubmissionStepConfig; import org.dspace.app.util.SubmissionStepConfig;
import org.dspace.content.Bitstream; import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat; import org.dspace.content.BitstreamFormat;
@@ -93,7 +94,7 @@ public class UploadStep extends org.dspace.submit.step.UploadStep
@Override @Override
public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig, public ErrorRest upload(Context context, SubmissionService submissionService, SubmissionStepConfig stepConfig,
InProgressSubmission wsi, MultipartFile file, String extraField) { InProgressSubmission wsi, MultipartFile file) {
Bitstream source = null; Bitstream source = null;
BitstreamFormat bf = null; BitstreamFormat bf = null;
@@ -113,7 +114,7 @@ public class UploadStep extends org.dspace.submit.step.UploadStep
source = bitstreamService.create(context, bundles.get(0), inputStream); source = bitstreamService.create(context, bundles.get(0), inputStream);
} }
source.setName(context, extraField); source.setName(context, Utils.getFileName(file));
source.setSource(context, file.getOriginalFilename()); source.setSource(context, file.getOriginalFilename());
// Identify the format // Identify the format

View File

@@ -165,6 +165,20 @@ public class Utils {
return org.dspace.core.Utils.standardize(schema, element, qualifier, "."); return org.dspace.core.Utils.standardize(schema, element, qualifier, ".");
} }
/**
* Create a temporary file from a multipart file upload
*
* @param multipartFile
* the multipartFile representing the uploaded file. Please note that it is a complex object including
* additional information other than the binary like the orginal file name and the mimetype
* @param prefixTempName
* the prefix to use to generate the filename of the temporary file
* @param suffixTempName
* the suffic to use to generate the filename of the temporary file
* @return the temporary file on the server
* @throws IOException
* @throws FileNotFoundException
*/
public static File getFile(MultipartFile multipartFile, String prefixTempName, String suffixTempName) public static File getFile(MultipartFile multipartFile, String prefixTempName, String suffixTempName)
throws IOException, FileNotFoundException { throws IOException, FileNotFoundException {
// TODO after change item-submission into // TODO after change item-submission into
@@ -183,4 +197,26 @@ public class Utils {
org.dspace.core.Utils.bufferedCopy(io, out); org.dspace.core.Utils.bufferedCopy(io, out);
return file; return file;
} }
/**
* Return the filename part from a multipartFile upload that could eventually contains the fullpath on the client
* filesystem
*
* @param multipartFile
* the file uploaded
* @return the filename part of the file on the client filesystem
* @throws IOException
* @throws FileNotFoundException
*/
public static String getFileName(MultipartFile multipartFile)
throws IOException, FileNotFoundException {
String originalFilename = multipartFile.getOriginalFilename();
if (originalFilename != null) {
// split by \ or / as we don't know the client OS (Win, Linux)
String[] parts = originalFilename.split("[\\/]");
return parts[parts.length - 1];
} else {
return multipartFile.getName();
}
}
} }

View File

@@ -486,12 +486,12 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
String authToken = getAuthToken(admin.getEmail(), password); String authToken = getAuthToken(admin.getEmail(), password);
InputStream bibtex = getClass().getResourceAsStream("bibtex-test.bib"); InputStream bibtex = getClass().getResourceAsStream("bibtex-test.bib");
final MockMultipartFile bibtexFile = new MockMultipartFile("file", bibtex); final MockMultipartFile bibtexFile = new MockMultipartFile("file", "bibtex-test.bib", "application/x-bibtex",
bibtex);
// bulk create workspaceitems in the default collection (col1) // bulk create workspaceitems in the default collection (col1)
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems") getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
.file(bibtexFile) .file(bibtexFile))
.param("extraField", "bibtex-test.bib"))
// bulk create should return 200, 201 (created) is better for single resource // bulk create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value", .andExpect(jsonPath("$._embedded.workspaceitems[0].sections.traditionalpageone['dc.title'][0].value",
@@ -506,6 +506,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
is("My Article 3"))) is("My Article 3")))
.andExpect( .andExpect(
jsonPath("$._embedded.workspaceitems[2]._embedded.collection.id", is(col1.getID().toString()))) jsonPath("$._embedded.workspaceitems[2]._embedded.collection.id", is(col1.getID().toString())))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
; ;
// bulk create workspaceitems explicitly in the col2 // bulk create workspaceitems explicitly in the col2
@@ -525,6 +527,8 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
is("My Article 3"))) is("My Article 3")))
.andExpect( .andExpect(
jsonPath("$._embedded.workspaceitems[2]._embedded.collection.id", is(col2.getID().toString()))) jsonPath("$._embedded.workspaceitems[2]._embedded.collection.id", is(col2.getID().toString())))
.andExpect(
jsonPath("$._embedded.workspaceitems[*]._embedded.upload").doesNotExist())
; ;
bibtex.close(); bibtex.close();
@@ -558,8 +562,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// bulk create a workspaceitem // bulk create a workspaceitem
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems") getClient(authToken).perform(fileUpload("/api/submission/workspaceitems")
.file(pdfFile) .file(pdfFile))
.param("extraField", "sample-article.pdf"))
// bulk create should return 200, 201 (created) is better for single resource // bulk create should return 200, 201 (created) is better for single resource
.andExpect(status().isOk()) .andExpect(status().isOk())
//FIXME it will be nice to setup a mock grobid server for end to end testing //FIXME it will be nice to setup a mock grobid server for end to end testing
@@ -569,7 +572,7 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
// we can just check that the pdf is stored in the item // we can just check that the pdf is stored in the item
.andExpect( .andExpect(
jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0].metadata['dc.title'][0].value", jsonPath("$._embedded.workspaceitems[0].sections.upload.files[0].metadata['dc.title'][0].value",
is("sample-article.pdf"))) is("myfile.pdf")))
.andExpect(jsonPath( .andExpect(jsonPath(
"$._embedded.workspaceitems[0].sections.upload.files[0].metadata['dc.source'][0].value", "$._embedded.workspaceitems[0].sections.upload.files[0].metadata['dc.source'][0].value",
is("/local/path/myfile.pdf"))) is("/local/path/myfile.pdf")))
@@ -1639,22 +1642,26 @@ public class WorkspaceItemRestRepositoryIT extends AbstractControllerIntegration
.build(); .build();
InputStream pdf = getClass().getResourceAsStream("simple-article.pdf"); InputStream pdf = getClass().getResourceAsStream("simple-article.pdf");
final MockMultipartFile pdfFile = new MockMultipartFile("file", pdf); final MockMultipartFile pdfFile = new MockMultipartFile("file", "/local/path/simple-article.pdf",
"application/pdf", pdf);
// upload the file in our workspaceitem // upload the file in our workspaceitem
getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem.getID()) getClient(authToken).perform(fileUpload("/api/submission/workspaceitems/" + witem.getID())
.file(pdfFile) .file(pdfFile))
.param("extraField", "sample-article.pdf"))
.andExpect(status().isCreated()) .andExpect(status().isCreated())
.andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
is("sample-article.pdf"))) is("simple-article.pdf")))
.andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value",
is("/local/path/simple-article.pdf")))
; ;
// check the file metadata // check the file metadata
getClient().perform(get("/api/submission/workspaceitems/" + witem.getID())) getClient().perform(get("/api/submission/workspaceitems/" + witem.getID()))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value", .andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.title'][0].value",
is("sample-article.pdf"))) is("simple-article.pdf")))
.andExpect(jsonPath("$.sections.upload.files[0].metadata['dc.source'][0].value",
is("/local/path/simple-article.pdf")))
; ;
} }