diff --git a/CHANGELOG.md b/CHANGELOG.md index 16cf4a5..81ef133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [v1.0.4-SNAPSHOT] 2020-11-11 Mongo integration with Concessione Project interface +TempFile management +WorkspaceContent for Concessioni-over-mongo + ## [v1.0.3] 2020-11-11 Fixed HTTP method diff --git a/src/main/java/org/gcube/application/geoportal/service/engine/ImplementationProvider.java b/src/main/java/org/gcube/application/geoportal/service/engine/ImplementationProvider.java index eb6f22a..b6059b5 100644 --- a/src/main/java/org/gcube/application/geoportal/service/engine/ImplementationProvider.java +++ b/src/main/java/org/gcube/application/geoportal/service/engine/ImplementationProvider.java @@ -41,6 +41,10 @@ public class ImplementationProvider { private EMFProvider emfProvider=new ScopedEMFProvider(); + @Getter + @Setter + private StorageHubProvider sHubProvider=new StorageHubProvider(); + public void shutdown() { // Stop JPA AbstractRecordManager.shutdown(); diff --git a/src/main/java/org/gcube/application/geoportal/service/engine/StorageHubProvider.java b/src/main/java/org/gcube/application/geoportal/service/engine/StorageHubProvider.java new file mode 100644 index 0000000..7aa2c8c --- /dev/null +++ b/src/main/java/org/gcube/application/geoportal/service/engine/StorageHubProvider.java @@ -0,0 +1,25 @@ +package org.gcube.application.geoportal.service.engine; + +import org.gcube.application.geoportal.model.fault.ConfigurationException; +import org.gcube.common.storagehub.client.dsl.StorageHubClient; + +public class StorageHubProvider implements Engine{ + + + @Override + public StorageHubClient getObject() throws ConfigurationException { + return new StorageHubClient(); + } + + @Override + public void init() { + // TODO Auto-generated method stub + + } + @Override + public void shustdown() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/org/gcube/application/geoportal/service/engine/WorkspaceManager.java b/src/main/java/org/gcube/application/geoportal/service/engine/WorkspaceManager.java new file mode 100644 index 0000000..e4cb836 --- /dev/null +++ b/src/main/java/org/gcube/application/geoportal/service/engine/WorkspaceManager.java @@ -0,0 +1,137 @@ +package org.gcube.application.geoportal.service.engine; + +import java.io.FileNotFoundException; +import java.io.InputStream; + +import javax.validation.constraints.NotNull; + +import org.gcube.application.geoportal.common.model.legacy.WorkspaceContent; +import org.gcube.application.geoportal.model.fault.ConfigurationException; +import org.gcube.common.storagehub.client.dsl.FileContainer; +import org.gcube.common.storagehub.client.dsl.FolderContainer; +import org.gcube.common.storagehub.client.dsl.StorageHubClient; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.Synchronized; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class WorkspaceManager { + + private static final String APP_FOLDER=".GNA_RECORDS"; + + + private StorageHubClient sgClient=null; + private FolderContainer appBase=null; + + @Getter + @Setter + @AllArgsConstructor + @RequiredArgsConstructor + public static class FolderOptions{ + @NotNull + private String folderName; + private String folderDescription; + private FolderContainer parent; + } + + @Getter + @Setter + @AllArgsConstructor + @RequiredArgsConstructor + public static class FileOptions{ + @NotNull + private String fileName; + @NonNull + private InputStream is; + + private String fileDescription; + private FolderContainer parent; + + } + + + public WorkspaceManager() throws ConfigurationException, StorageHubException { + sgClient= ImplementationProvider.get().getSHubProvider().getObject(); + appBase=getApplicationBaseFolder(sgClient); + } + + public FolderContainer createFolder(FolderOptions opts) throws StorageHubException { + if(opts.getParent()==null) + opts.setParent(appBase); + return createFolder(opts,sgClient); + } + + public FileContainer getFileById(String id) throws StorageHubException { + return sgClient.open(id).asFile(); + } + + public FolderContainer getFolderById(String id) throws StorageHubException { + return sgClient.open(id).asFolder(); + } + + public FolderContainer getSubFolder(FolderContainer parentFolder,String path) throws StorageHubException { + try{ + return parentFolder.openByRelativePath(path).asFolder(); + }catch(StorageHubException e) { + log.debug("Missing subPath "+path); + FolderContainer targetParent=parentFolder; + String targetName=path; + if(path.contains("/")) { + String parent=path.substring(0, path.lastIndexOf("/")); + log.debug("Checking intermediate "+parent); + targetParent=getSubFolder(parentFolder,parent); + targetName=path.substring(path.lastIndexOf("/")+1); + } + log.debug("Creating "+targetName); + return createFolder(new FolderOptions(targetName,"",targetParent),sgClient); + } + } + + + public WorkspaceContent storeToWS(FileOptions opts) throws FileNotFoundException, StorageHubException { + FileContainer item=createFile(opts,sgClient); + item=sgClient.open(item.getId()).asFile(); + + WorkspaceContent content=new WorkspaceContent(); + content.setLink(item.getPublicLink().toString()); + content.setMimetype(item.get().getContent().getMimeType()); + content.setStorageID(item.getId()); + return content; + + } + + public void deleteFromWS(WorkspaceContent toDelete) throws StorageHubException { + sgClient.open(toDelete.getStorageID()).asFile().forceDelete(); + } + + // STATIC SYNCH METHODS + + @Synchronized + private static FolderContainer getApplicationBaseFolder(StorageHubClient sgClient) throws StorageHubException { + FolderContainer vre=sgClient.openVREFolder(); + try { + return vre.openByRelativePath(APP_FOLDER).asFolder(); + }catch(StorageHubException e) { + log.debug("APP Fodler missing. Initializing.."); + FolderContainer toReturn= vre.newFolder(APP_FOLDER, "Base folder for GNA records"); + toReturn.setHidden(); + return toReturn; + } + } + + @Synchronized + private static FolderContainer createFolder(FolderOptions opts, StorageHubClient sgClient) throws StorageHubException { + return opts.getParent().newFolder(opts.getFolderName(),opts.getFolderDescription()); + } + + @Synchronized + private static FileContainer createFile(FileOptions opts, StorageHubClient sgClient) throws StorageHubException { + return opts.getParent().uploadFile(opts.getIs(), opts.getFileName(), opts.getFileDescription()); + } +} diff --git a/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ConcessioniMongoManager.java b/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ConcessioniMongoManager.java index f49fe00..b015780 100644 --- a/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ConcessioniMongoManager.java +++ b/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ConcessioniMongoManager.java @@ -1,15 +1,32 @@ package org.gcube.application.geoportal.service.engine.mongo; import java.io.IOException; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import org.bson.Document; import org.bson.types.ObjectId; +import org.gcube.application.geoportal.common.model.legacy.AssociatedContent; import org.gcube.application.geoportal.common.model.legacy.Concessione; +import org.gcube.application.geoportal.common.model.legacy.LayerConcessione; +import org.gcube.application.geoportal.common.model.legacy.OtherContent; +import org.gcube.application.geoportal.common.model.legacy.PersistedContent; +import org.gcube.application.geoportal.common.model.legacy.RelazioneScavo; +import org.gcube.application.geoportal.common.model.legacy.SDILayerDescriptor; +import org.gcube.application.geoportal.common.model.legacy.UploadedImage; +import org.gcube.application.geoportal.common.model.legacy.WorkspaceContent; import org.gcube.application.geoportal.common.model.legacy.report.PublicationReport; +import org.gcube.application.geoportal.common.rest.TempFile; import org.gcube.application.geoportal.model.fault.ConfigurationException; +import org.gcube.application.geoportal.service.engine.ImplementationProvider; +import org.gcube.application.geoportal.service.engine.StorageClientProvider; +import org.gcube.application.geoportal.service.engine.WorkspaceManager; +import org.gcube.application.geoportal.service.engine.WorkspaceManager.FileOptions; +import org.gcube.application.geoportal.service.engine.WorkspaceManager.FolderOptions; import org.gcube.application.geoportal.service.utils.Serialization; +import org.gcube.common.storagehub.client.dsl.FolderContainer; +import org.gcube.common.storagehub.model.exceptions.StorageHubException; import com.fasterxml.jackson.core.JsonProcessingException; import com.mongodb.client.MongoDatabase; @@ -58,11 +75,11 @@ public class ConcessioniMongoManager extends MongoManager{ Concessione toReturn=asConcessione(getById(id,collectionName)); toReturn.setMongo_id(id.toHexString()); - return asConcessione(update(asDocument(toReturn),collectionName)); + return asConcessione(replace(asDocument(toReturn),collectionName)); } public Concessione update(Concessione toRegister) throws IOException { - return asConcessione(update(asDocument(toRegister),collectionName)); + return asConcessione(replace(asDocument(toRegister),collectionName)); } public List list(){ @@ -91,11 +108,66 @@ public class ConcessioniMongoManager extends MongoManager{ toReturn.setDefaults(); toReturn.validate(); publish(toReturn); - return asConcessione(update(asDocument(toReturn),collectionName)); + return asConcessione(replace(asDocument(toReturn),collectionName)); + } + + + public Concessione persistContent(String id, String destinationPath, List files) throws Exception { + Concessione c = getById(id); + WorkspaceManager ws=new WorkspaceManager(); + c.setDefaults(); + + //Check Init Base folder + FolderContainer baseFolder=null; + if(c.getFolderId()==null) { + String folderName="mConcessione"+"_"+c.getNome()+"_"+Serialization.FULL_FORMATTER.format(LocalDateTime.now()); + log.info("Creating folder {} for Concessione ID {} ",folderName,id); + FolderContainer folder=ws.createFolder(new FolderOptions(folderName, "Base Folder for "+c.getNome(),null)); + c.setFolderId(folder.getId()); + } + + baseFolder=ws.getFolderById(c.getFolderId()); + + AssociatedContent section=c.getContentByPath(destinationPath); + + store(section,files,ws,baseFolder); + return asConcessione(replace(asDocument(c),collectionName)); } private static PublicationReport publish(Concessione c) { //TODO implement return null; } + + private static final void store(AssociatedContent content,List files, WorkspaceManager ws, FolderContainer base) throws Exception { + FolderContainer sectionParent=null; + + if(content instanceof RelazioneScavo) + sectionParent = ws .createFolder(new FolderOptions( + "relazione","Relazione di scavo : "+content.getTitolo(),base)); + + else if (content instanceof UploadedImage) + sectionParent = ws .createFolder(new FolderOptions( + "imgs","Immagini rappresentative : "+content.getTitolo(),base)); + + else if (content instanceof SDILayerDescriptor) + //SDI Section + if(content instanceof LayerConcessione) + sectionParent = ws .createFolder(new FolderOptions( + content.getTitolo(),"Layer Concessione : "+content.getTitolo(),ws.getSubFolder(base,"layers"))); + else throw new Exception("Invalid SDI Content "+content); + else if (content instanceof OtherContent ) + sectionParent = ws .createFolder(new FolderOptions( + content.getTitolo(),"Relazione di scavo : "+content.getTitolo(),ws.getSubFolder(base,"other"))); + else throw new Exception("Invalid Content "+content); + + content.setActualContent(new ArrayList()); + StorageClientProvider storage=ImplementationProvider.get().getStorageProvider(); + for(TempFile f : files) { + WorkspaceContent wsContent=ws.storeToWS(new FileOptions(f.getFilename(), storage.open(f.getId()), "Imported via GeoPortal", sectionParent)); + log.debug("Registered "+wsContent+" for "+content); + content.getActualContent().add(wsContent); + } + } + } diff --git a/src/main/java/org/gcube/application/geoportal/service/engine/mongo/MongoManager.java b/src/main/java/org/gcube/application/geoportal/service/engine/mongo/MongoManager.java index 354575e..6ff0374 100644 --- a/src/main/java/org/gcube/application/geoportal/service/engine/mongo/MongoManager.java +++ b/src/main/java/org/gcube/application/geoportal/service/engine/mongo/MongoManager.java @@ -92,7 +92,7 @@ public abstract class MongoManager { return coll.find(filter,clazz); } - public Document update(Document toUpdate,String collectionName) { + public Document replace(Document toUpdate,String collectionName) { MongoDatabase database=getDatabase(); MongoCollection coll=database.getCollection(collectionName); return coll.findOneAndReplace( diff --git a/src/main/java/org/gcube/application/geoportal/service/rest/ConcessioniOverMongo.java b/src/main/java/org/gcube/application/geoportal/service/rest/ConcessioniOverMongo.java index c017a6b..1883db2 100644 --- a/src/main/java/org/gcube/application/geoportal/service/rest/ConcessioniOverMongo.java +++ b/src/main/java/org/gcube/application/geoportal/service/rest/ConcessioniOverMongo.java @@ -1,6 +1,6 @@ package org.gcube.application.geoportal.service.rest; -import java.util.List; +import java.time.LocalDateTime; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -14,10 +14,14 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import org.gcube.application.geoportal.common.model.legacy.Concessione; +import org.gcube.application.geoportal.common.rest.AddSectionToConcessioneRequest; import org.gcube.application.geoportal.common.rest.InterfaceConstants; import org.gcube.application.geoportal.common.rest.TempFile; +import org.gcube.application.geoportal.service.engine.WorkspaceManager; +import org.gcube.application.geoportal.service.engine.WorkspaceManager.FolderOptions; import org.gcube.application.geoportal.service.engine.mongo.ConcessioniMongoManager; import org.gcube.application.geoportal.service.utils.Serialization; +import org.gcube.common.storagehub.client.dsl.FolderContainer; import org.json.JSONArray; import org.json.JSONObject; @@ -31,7 +35,7 @@ public class ConcessioniOverMongo { @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - public String update(String jsonString) { + public String replace(String jsonString) { return new GuardedMethod () { @Override protected String run() throws Exception, WebApplicationException { @@ -111,6 +115,26 @@ public class ConcessioniOverMongo { }.execute(); } + + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("{"+InterfaceConstants.Parameters.PROJECT_ID+"}") + public String replace(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id,String jsonString) { + return new GuardedMethod () { + @Override + protected String run() throws Exception, WebApplicationException { +// Concessione c=Serialization.read(jsonString, Concessione.class); +// ConcessioniMongoManager manager=new ConcessioniMongoManager(); +// manager.update(c); +// +// return Serialization.write(manager.getById(c.getMongo_id())); + throw new RuntimeException("TO IMPLEMENT"); + } + }.execute().getResult(); + } + + @PUT @Produces(MediaType.APPLICATION_JSON) @Path("/publish/{"+InterfaceConstants.Parameters.PROJECT_ID+"}") @@ -128,14 +152,19 @@ public class ConcessioniOverMongo { @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/registerFiles/{"+InterfaceConstants.Parameters.PROJECT_ID+"}") - public String registerFile(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id,List files) { + public String registerFile(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id,String jsonRequest) { return new GuardedMethod () { @Override protected String run() throws Exception, WebApplicationException { - //TODO FILE register + AddSectionToConcessioneRequest request=Serialization.read(jsonRequest,AddSectionToConcessioneRequest.class); + log.info("Registering {} file(s) for {} Concessione ID {}", + request.getStreams().size(), + request.getDestinationPath(),id); + ConcessioniMongoManager manager=new ConcessioniMongoManager(); + Concessione toReturn= manager.persistContent(id, request.getDestinationPath(), request.getStreams()); - ConcessioniMongoManager manager=new ConcessioniMongoManager(); - return Serialization.write(manager.publish(id)); + log.debug("Returning "+toReturn); + return Serialization.write(toReturn); } }.execute().getResult(); } diff --git a/src/main/java/org/gcube/application/geoportal/service/utils/Serialization.java b/src/main/java/org/gcube/application/geoportal/service/utils/Serialization.java index 66c9013..08dbaff 100644 --- a/src/main/java/org/gcube/application/geoportal/service/utils/Serialization.java +++ b/src/main/java/org/gcube/application/geoportal/service/utils/Serialization.java @@ -1,6 +1,7 @@ package org.gcube.application.geoportal.service.utils; import java.io.IOException; +import java.time.format.DateTimeFormatter; import org.gcube.application.geoportal.model.Record; import org.gcube.application.geoportal.model.concessioni.Concessione; @@ -17,6 +18,9 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; public class Serialization { + + public static final DateTimeFormatter FULL_FORMATTER=DateTimeFormatter.ofPattern("uuuuMMdd_HH-mm-ss"); + public static ObjectMapper mapper; static { diff --git a/src/test/java/org/gcube/application/geoportal/service/ConcessioniOverMongoTest.java b/src/test/java/org/gcube/application/geoportal/service/ConcessioniOverMongoTest.java index fb7840c..5184691 100644 --- a/src/test/java/org/gcube/application/geoportal/service/ConcessioniOverMongoTest.java +++ b/src/test/java/org/gcube/application/geoportal/service/ConcessioniOverMongoTest.java @@ -1,6 +1,10 @@ package org.gcube.application.geoportal.service; -import java.io.IOException; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.FileInputStream; +import java.util.Collections; import java.util.List; import javax.ws.rs.client.Entity; @@ -9,8 +13,15 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.gcube.application.geoportal.common.model.legacy.Concessione; +import org.gcube.application.geoportal.common.model.legacy.Concessione.Paths; +import org.gcube.application.geoportal.common.rest.AddSectionToConcessioneRequest; +import org.gcube.application.geoportal.common.rest.TempFile; +import org.gcube.application.geoportal.common.utils.Files; +import org.gcube.application.geoportal.common.utils.StorageUtils; +import org.gcube.application.geoportal.service.legacy.TokenSetter; import org.gcube.application.geoportal.service.utils.Serialization; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; public class ConcessioniOverMongoTest extends BasicServiceTestUnit{ @@ -22,6 +33,11 @@ public class ConcessioniOverMongoTest extends BasicServiceTestUnit{ private static final String FILES_PATH="registerFiles"; + @Before + public void setContext() { + TokenSetter.set("/gcube/devsec/devVRE"); + } + @Test public void list() { WebTarget target=target(PATH); @@ -92,8 +108,32 @@ public class ConcessioniOverMongoTest extends BasicServiceTestUnit{ } @Test - public void uploadFile() { - // TODO + public void uploadFile() throws Exception { + WebTarget target=target(PATH); + Response resp=target.request(MediaType.APPLICATION_JSON).post(Entity.entity(Serialization.write(TestModel.prepareEmptyConcessione()), MediaType.APPLICATION_JSON)); + Concessione c=check(resp,Concessione.class); + Assert.assertTrue(c.getMongo_id()!=null&&!c.getMongo_id().isEmpty()); + System.out.println("ID IS "+c.getMongo_id()); + + // Insert section + c.setRelazioneScavo(TestModel.prepareConcessione().getRelazioneScavo()); +// c.getRelazioneScavo().setMongo_id(TestModel.rnd()); + + resp=target.request(MediaType.APPLICATION_JSON).put(Entity.entity(Serialization.write(c), MediaType.APPLICATION_JSON)); + c=check(resp,Concessione.class); + + // Add file + TempFile f=new StorageUtils().putOntoStorage(new FileInputStream(Files.getFileFromResources("concessioni/relazione.pdf")), "relazione.pdf"); + AddSectionToConcessioneRequest request=new AddSectionToConcessioneRequest(); + request.setDestinationPath(Paths.RELAZIONE); + request.setStreams(Collections.singletonList(f)); + + resp=target.path(FILES_PATH).path(c.getMongo_id()).request(MediaType.APPLICATION_JSON).post(Entity.entity(Serialization.write(request), MediaType.APPLICATION_JSON)); + c=check(resp,Concessione.class); + assertNotNull(c.getRelazioneScavo().getActualContent()); + assertTrue(c.getRelazioneScavo().getActualContent().size()>0); + + System.out.println("ADDED FILE TO "+c); } @Test diff --git a/src/test/java/org/gcube/application/geoportal/service/TestModel.java b/src/test/java/org/gcube/application/geoportal/service/TestModel.java index 2f478b8..25aef77 100644 --- a/src/test/java/org/gcube/application/geoportal/service/TestModel.java +++ b/src/test/java/org/gcube/application/geoportal/service/TestModel.java @@ -3,7 +3,9 @@ package org.gcube.application.geoportal.service; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.UUID; +import org.bson.types.ObjectId; import org.gcube.application.geoportal.common.model.legacy.AccessPolicy; import org.gcube.application.geoportal.common.model.legacy.Concessione; import org.gcube.application.geoportal.common.model.legacy.LayerConcessione; @@ -57,6 +59,19 @@ public class TestModel { return concessione; } + public static final Concessione setIds(Concessione c) { +// c.setMongo_id(rnd()); + c.getRelazioneScavo().setMongo_id(rnd()); + c.getPosizionamentoScavo().setMongo_id(rnd()); + c.getPianteFineScavo().forEach((LayerConcessione l)->{l.setMongo_id(rnd());}); + c.getImmaginiRappresentative().forEach((UploadedImage i)->{i.setMongo_id(rnd());}); + return c; + } + + public static final String rnd() { + return new ObjectId().toHexString(); + } + public static Concessione prepareConcessione() { Concessione concessione=prepareEmptyConcessione();