From 3d6f86ef91a5aa98eea1d34f88908029039f5755 Mon Sep 17 00:00:00 2001 From: Fabio Sinibaldi Date: Tue, 29 Mar 2022 18:06:09 +0200 Subject: [PATCH] Default LC Plugins refactoring --- .../cms/plugins/IndexerPluginInterface.java | 3 + .../cms/plugins/MaterializationPlugin.java | 2 + .../faults/UnrecognizedEventException.java | 22 + .../faults/UnrecognizedStepException.java | 22 + .../AbstractLifeCycleManager.java | 188 ++++++-- .../implementations/Default3PhaseManager.java | 412 +++++++----------- .../implementations/GuardedEventManager.java | 15 + .../implementations/GuardedExecution.java | 35 ++ .../implementations/GuardedStepExecution.java | 7 + .../plugins/implementations/RoleManager.java | 60 +++ .../SimpleLifeCycleManager.java | 238 ++++++++++ .../cms/plugins/model/StepAccess.java | 29 -- .../reports/DocumentHandlingReport.java | 7 + .../plugins/reports/StepExecutionReport.java | 5 +- .../requests/EventExecutionRequest.java | 7 +- .../cms/tests/model/DummyPlugin.java | 10 + .../lifecycle/LifecycleInformation.java | 6 +- .../service/engine/mongo/MongoManagerI.java | 2 +- .../engine/mongo/ProfiledMongoManager.java | 137 +++--- .../service/engine/mongo/LockTests.java | 4 +- .../sdi/plugins/SDIMaterializerPlugin.java | 5 + 21 files changed, 821 insertions(+), 395 deletions(-) create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedEventException.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedStepException.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedEventManager.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedExecution.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedStepExecution.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/RoleManager.java create mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/SimpleLifeCycleManager.java delete mode 100644 cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/IndexerPluginInterface.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/IndexerPluginInterface.java index c6940ce..4ac15ea 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/IndexerPluginInterface.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/IndexerPluginInterface.java @@ -12,7 +12,10 @@ import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDes public interface IndexerPluginInterface extends InitializablePlugin{ public IndexDocumentReport index(IndexDocumentRequest request) throws InvalidPluginRequestException; + public IndexDocumentReport deindex(IndexDocumentRequest request) throws InvalidPluginRequestException; public Index getIndex(BaseRequest request) throws ConfigurationException; + + } diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/MaterializationPlugin.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/MaterializationPlugin.java index 0e4c3df..d1e796c 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/MaterializationPlugin.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/MaterializationPlugin.java @@ -9,4 +9,6 @@ public interface MaterializationPlugin extends InitializablePlugin{ public MaterializationReport materialize(MaterializationRequest request) throws MaterializationException, InvalidPluginRequestException; + public MaterializationReport dematerialize(MaterializationRequest request) throws MaterializationException, InvalidPluginRequestException; + } diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedEventException.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedEventException.java new file mode 100644 index 0000000..1586575 --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedEventException.java @@ -0,0 +1,22 @@ +package org.gcube.application.cms.plugins.faults; + +public class UnrecognizedEventException extends EventException{ + public UnrecognizedEventException() { + } + + public UnrecognizedEventException(String message) { + super(message); + } + + public UnrecognizedEventException(String message, Throwable cause) { + super(message, cause); + } + + public UnrecognizedEventException(Throwable cause) { + super(cause); + } + + public UnrecognizedEventException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedStepException.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedStepException.java new file mode 100644 index 0000000..8dd57db --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/faults/UnrecognizedStepException.java @@ -0,0 +1,22 @@ +package org.gcube.application.cms.plugins.faults; + +public class UnrecognizedStepException extends StepException{ + public UnrecognizedStepException() { + } + + public UnrecognizedStepException(String message) { + super(message); + } + + public UnrecognizedStepException(String message, Throwable cause) { + super(message, cause); + } + + public UnrecognizedStepException(Throwable cause) { + super(cause); + } + + public UnrecognizedStepException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java index ef17385..56e64e0 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/AbstractLifeCycleManager.java @@ -5,15 +5,11 @@ import lombok.extern.slf4j.Slf4j; import org.gcube.application.cms.plugins.LifecycleManager; import org.gcube.application.cms.plugins.PluginManagerInterface; import org.gcube.application.cms.plugins.faults.*; -import org.gcube.application.cms.plugins.model.StepAccess; -import org.gcube.application.cms.plugins.reports.EventExecutionReport; -import org.gcube.application.cms.plugins.reports.InitializationReport; -import org.gcube.application.cms.plugins.reports.Report; -import org.gcube.application.cms.plugins.reports.StepExecutionReport; +import org.gcube.application.cms.plugins.model.PluginDescriptor; +import org.gcube.application.cms.plugins.reports.*; import org.gcube.application.cms.plugins.requests.BaseRequest; import org.gcube.application.cms.plugins.requests.EventExecutionRequest; import org.gcube.application.cms.plugins.requests.StepExecutionRequest; -import org.gcube.application.cms.serialization.Serialization; import org.gcube.application.geoportal.common.model.configuration.Configuration; import org.gcube.application.geoportal.common.model.document.accounting.User; import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation; @@ -22,7 +18,8 @@ import org.gcube.application.geoportal.common.model.rest.ConfigurationException; import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration; import org.gcube.application.geoportal.common.utils.ContextUtils; -import java.util.List; +import java.util.HashMap; +import java.util.Map; @Slf4j public abstract class AbstractLifeCycleManager extends AbstractPlugin implements LifecycleManager { @@ -30,43 +27,116 @@ public abstract class AbstractLifeCycleManager extends AbstractPlugin implements @Setter protected PluginManagerInterface pluginManager; + private Map registeredSteps=new HashMap<>(); + private Map registeredEvent=new HashMap<>(); + + + protected void setEvent(String event,GuardedEventManager m){registeredEvent.put(event,m);} + protected void setStep(String step,GuardedStepExecution e){registeredSteps.put(step,e);} + + private GuardedEventManager defaultUpdateManager= new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + theReport = validate(theReport); + theReport = setDefault(theReport); + return theReport; + } + }; + + private GuardedEventManager noOp = new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + return theReport; + } + }; + + + public AbstractLifeCycleManager() { + registerEvents(); + registerSteps(); + } + + protected EventExecutionReport onDeleteDocument(EventExecutionReport report) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException {return report;} + protected EventExecutionReport onDeleteFileSet(EventExecutionReport report) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException {return report;} + protected EventExecutionReport onUpdateDocument(EventExecutionReport report){ + report = validate(report); + report = setDefault(report); + return report; + } + protected EventExecutionReport onInitDocument(EventExecutionReport report) throws InvalidPluginRequestException { + report = validate(report); + report = setDefault(report); + return report; + } + + + + protected void registerEvents(){ + setEvent(EventExecutionRequest.Events.ON_INIT_DOCUMENT, new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + return onInitDocument(theReport); + } + }); + setEvent(EventExecutionRequest.Events.ON_UPDATE_DOCUMENT, new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + return onUpdateDocument(theReport); + } + }); + setEvent(EventExecutionRequest.Events.ON_DELETE_DOCUMENT, new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + return onDeleteDocument(theReport); + } + }); + setEvent(EventExecutionRequest.Events.ON_DELETE_FILESET, new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + return onDeleteFileSet(theReport); + } + }); + } + protected void registerSteps(){} + + + protected PluginDescriptor DESCRIPTOR=new PluginDescriptor(";;;", PluginDescriptor.BaseTypes.LIFECYCLE_MANAGER); + @Override public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException, InvalidProfileException, ConfigurationException, InsufficientPrivileges { log.info("Serving Request {}",request); StepExecutionReport report=new StepExecutionReport(request); report.setStatus(Report.Status.OK); + LifecycleInformation info=report.getToSetLifecycleInformation(); + info.setLastOperationStatus(LifecycleInformation.Status.OK); + info.setLastInvokedStep(request.getStep()); if(!canInvokeStep(request.getStep(),request.getCaller(), getConfigurationFromProfile(request.getUseCaseDescriptor()))) throw new InsufficientPrivileges("User is not allowed to invoke "+request.getStep()); + if(!registeredSteps.containsKey(request.getStep())) + throw new UnrecognizedStepException(("Invalid Step " + request.getStep())); - LifecycleInformation info=report.getToSetLifecycleInformation(); - info.setLastOperationStatus(LifecycleInformation.Status.OK); - info.setLastInvokedStep(request.getStep()); - + try { + return registeredSteps.get(request.getStep()) + .setTheReport(report).execute(); + }catch (StepException | InvalidPluginRequestException e){ + throw e; + }catch (Throwable t) { + log.error("Unable to perform step " + request.getStep(), t); + String msg = "Unable to execute Step " + request.getStep() + ". Error was " + t.getMessage(); + report.setStatus(Report.Status.ERROR); + report.putMessage(msg); + info.setLastOperationStatus(LifecycleInformation.Status.ERROR); + info.addErrorMessage(msg); + } return report; } - - protected static boolean canInvokeStep(String stepID, User u, HandlerDeclaration config) throws ConfigurationException { - log.debug("Checking if {} can access STEP {}",u,stepID); - log.trace("Config is {}",config); - List l =config.getConfiguration().get("step_access", List.class); - if(l==null|| l.isEmpty()) throw new ConfigurationException("Missing Role management in UCD"); - for (Object o : l) { - StepAccess a= Serialization.convert(o,StepAccess.class); - if(a.getStepId().equals(stepID)){ - // found step ID - log.trace("Found Step ID declaration {} ",a); - for (String s : a.getRoles()) { - if (u.getRoles().contains(s)) - return true; - } - } - } - return false; + protected boolean canInvokeStep(String stepID, User u, HandlerDeclaration config) throws ConfigurationException { + return new RoleManager(config).canInvokeStep(stepID,u); } @@ -97,6 +167,53 @@ public abstract class AbstractLifeCycleManager extends AbstractPlugin implements return report; } + + + @Override + public PluginDescriptor getDescriptor() { + return DESCRIPTOR; + } + + @Override + public EventExecutionReport onEvent(EventExecutionRequest request) throws EventException, InvalidPluginRequestException { + EventExecutionReport report=new EventExecutionReport(request); + TriggeredEvents info = report.getToSetLifecycleInformation().getLastEvent(); + try { + if(!registeredEvent.containsKey(request.getEvent())) + throw new UnrecognizedEventException("Unexpected Event "+request.getEvent()); + + return registeredEvent.get(request.getEvent()).setTheReport(report).execute(); + + }catch (EventException e){ + throw e; + }catch (Throwable t){ + log.error("Unable to execute on event "+request.getEvent(),t); + String msg = "Unable to execute on event "+request.getEvent()+". Error was "+t.getMessage(); + info.setLastOperationStatus(LifecycleInformation.Status.ERROR); + info.addErrorMessage(msg); + report.setStatus(Report.Status.ERROR); + report.putMessage(msg); + } + return report; + } + + + /** + * Override this method for programmatic default values management + * + * @param currentReport + * @return + */ + public EventExecutionReport setDefault(EventExecutionReport currentReport){ + // Default implementation is no op + return currentReport; + } + + public EventExecutionReport validate(EventExecutionReport currentReport){ + // Default implementation is no op + return currentReport; + } + @Override public void shutdown() throws ShutDownException {} @@ -104,17 +221,4 @@ public abstract class AbstractLifeCycleManager extends AbstractPlugin implements public Configuration getCurrentConfiguration(BaseRequest request) throws ConfigurationException { return new Configuration(); } - - @Override - public EventExecutionReport onEvent(EventExecutionRequest request) throws EventException, InvalidPluginRequestException { - log.info("Executing Event {}",request); - EventExecutionReport report=new EventExecutionReport(request); - - TriggeredEvents info=new TriggeredEvents(); - info.setEvent(request.getEvent()); - info.setLastOperationStatus(LifecycleInformation.Status.OK); - report.setStatus(Report.Status.OK); - report.getToSetLifecycleInformation().addEventReport(info); - return report; - } } diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java index 380f8da..5060a9c 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/Default3PhaseManager.java @@ -1,41 +1,33 @@ package org.gcube.application.cms.plugins.implementations; -import com.sun.xml.internal.xsom.impl.scd.Step; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.gcube.application.cms.plugins.IndexerPluginInterface; import org.gcube.application.cms.plugins.MaterializationPlugin; -import org.gcube.application.cms.plugins.implementations.AbstractLifeCycleManager; import org.gcube.application.cms.plugins.LifecycleManager; import org.gcube.application.cms.plugins.faults.*; import org.gcube.application.cms.plugins.model.PluginDescriptor; -import org.gcube.application.cms.plugins.model.StepAccess; import org.gcube.application.cms.plugins.reports.*; import org.gcube.application.cms.plugins.requests.*; import org.gcube.application.cms.plugins.model.ComparableVersion; -import org.gcube.application.cms.serialization.Serialization; import org.gcube.application.geoportal.common.model.configuration.Configuration; import org.gcube.application.geoportal.common.model.configuration.Index; -import org.gcube.application.geoportal.common.model.document.accounting.User; import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation; import org.gcube.application.geoportal.common.model.document.lifecycle.TriggeredEvents; import org.gcube.application.geoportal.common.model.rest.ConfigurationException; -import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration; import org.gcube.application.geoportal.common.utils.Files; import java.util.ArrayList; -import java.util.List; @Slf4j -public class Default3PhaseManager extends AbstractLifeCycleManager implements LifecycleManager { +public class Default3PhaseManager extends SimpleLifeCycleManager implements LifecycleManager { private static class Phases { public static final String PENDING_APPROVAL="Pending Approval"; - public static final String PUBLISHED="PUBLISHED"; + public static final String PUBLISHED="Published"; } - private static class STEPS{ public static final String SUBMIT="SUBMIT-FOR-REVIEW"; public static final String REJECT="REJECT-DRAFT"; @@ -46,53 +38,186 @@ public class Default3PhaseManager extends AbstractLifeCycleManager implements Li public static final String NOTES="notes"; } + @Override + protected EventExecutionReport onDeleteDocument(EventExecutionReport report) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException { + report = super.onDeleteDocument(report); + return deIndex(report,getIndexer(),getInternalIndexParams(report.getTheRequest())); + } - protected PluginDescriptor DESCRIPTOR=new PluginDescriptor("DEFAULT-3PHASE", PluginDescriptor.BaseTypes.LIFECYCLE_MANAGER); + @Override + protected EventExecutionReport onDeleteFileSet(EventExecutionReport theReport) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException { + theReport = super.onDeleteFileSet(theReport); + String phase = theReport.getTheRequest().getDocument().getLifecycleInformation().getPhase(); + if(phase.equals(Phases.PENDING_APPROVAL)) + return index(theReport,getIndexer(),getInternalIndexParams(theReport.getTheRequest())); + if(phase.equals(Phases.PUBLISHED)) + return index(theReport,getIndexer(),getPublicIndexParams(theReport.getTheRequest())); + return theReport; + } + + @Override + protected void registerSteps() { + // register steps + setStep(STEPS.SUBMIT, new GuardedStepExecution() { + @Override + protected StepExecutionReport run() throws Exception { + if(!theReport.getTheRequest().getDocument().getLifecycleInformation().getPhase().equals(LifecycleInformation.CommonPhases.DRAFT_PHASE)) + throw new StepException("Document is not in "+LifecycleInformation.CommonPhases.DRAFT_PHASE+" phase"); + // Materialize + theReport = materializeDocument(theReport,getMaterializer(),getMaterializationParameters(theReport.getTheRequest())); + if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)){ + // Index + theReport = index(theReport,getIndexer(), + getInternalIndexParams(theReport.getTheRequest())); + // setPhase + if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) + theReport.getToSetLifecycleInformation().setPhase(Phases.PENDING_APPROVAL); + } + return theReport; + } + }); + + + setStep(STEPS.APPROVE, new GuardedStepExecution() { + @Override + protected StepExecutionReport run() throws Exception { + if(!theReport.getTheRequest().getDocument().getLifecycleInformation().getPhase().equals(Phases.PENDING_APPROVAL)) + throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); + + // Index + theReport = index(theReport,getIndexer(), + getPublicIndexParams(theReport.getTheRequest())); + // setPhase + if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) + theReport.getToSetLifecycleInformation().setPhase(Phases.PUBLISHED); + return theReport; + } + }); + + setStep(STEPS.REJECT, new GuardedStepExecution() { + @Override + protected StepExecutionReport run() throws Exception { + if(!theReport.getTheRequest().getDocument().getLifecycleInformation().getPhase().equals(Phases.PENDING_APPROVAL)) + throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); + if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) + theReport.getToSetLifecycleInformation().setPhase(LifecycleInformation.CommonPhases.DRAFT_PHASE); + return theReport; + } + }); + } public Default3PhaseManager() { DESCRIPTOR.setDescription("Default 3-phase lifecycle manager. This plugin supports a simple moderated publication lifecycle."); DESCRIPTOR.setVersion(new ComparableVersion("1.0.0")); } - protected static boolean canInvokeStep(String stepID, User u, HandlerDeclaration config) throws ConfigurationException { - List l =config.getConfiguration().get("step_access", List.class); - if(l==null|| l.isEmpty()) throw new ConfigurationException("Missing Role management in UCD"); - boolean existingRule=false; - for (Object o : l) { - StepAccess a= Serialization.convert(o, StepAccess.class); - if(a.getStepId().equals(stepID)) { - existingRule = true; - log.trace("Found role {}",a); - for (String role : a.getRoles()) - if (u.getRoles().contains(role)) - return true; - } - } - if(!existingRule) throw new ConfigurationException("Missing step "+stepID+" access definition"); - // nothing matches - return false; - } + + + +// @Override +// public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException, InvalidProfileException, ConfigurationException, InsufficientPrivileges { +// StepExecutionReport report = super.performStep(request); +// LifecycleInformation info=report.getToSetLifecycleInformation(); +// try { +// if(!canInvokeStep(request.getStep(),request.getCaller(),getConfigurationFromProfile(request.getUseCaseDescriptor()))) +// throw new InsufficientPrivileges("Insufficient privileges for executing step "+request.getStep()); +// +// MaterializationPlugin plugin; +// IndexerPluginInterface indexerPlugin; +// plugin= (MaterializationPlugin) pluginManager.getById("SDI-Default-Materializer"); +// indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); +// +// +// +// switch (request.getStep()) { +// case STEPS.SUBMIT:{ +// //TODO validation +// +// if(!request.getDocument().getLifecycleInformation().getPhase().equals(LifecycleInformation.CommonPhases.DRAFT_PHASE)) +// throw new StepException("Document is not in "+LifecycleInformation.CommonPhases.DRAFT_PHASE+" phase"); +// //Materialize layers +// MaterializationRequest matReq = new MaterializationRequest(request.getUseCaseDescriptor(),request.getCaller(), request.getContext(), request.getDocument()); +// +// matReq.setDocument(request.getDocument()); +// matReq.setUseCaseDescriptor(request.getUseCaseDescriptor()); +// Document params = new Document(); +// String workspace = request.getUseCaseDescriptor().getId() + request.getContext().getId(); +// params.put("workspace", Files.fixFilename(workspace)); +// +// +// matReq.setCallParameters(params); +// MaterializationReport matRep = plugin.materialize(matReq); +// +// +// report.setResultingDocument(matRep.getResultingDocument()); +// +// +// switch(matRep.getStatus()){ +// case OK : { +// index(report,indexerPlugin); +// //TODO Optional Notifications +// break; +// } +// case ERROR : { +// info.setLastOperationStatus(LifecycleInformation.Status.ERROR); +// matRep.getMessages().forEach(s -> info.addErrorMessage(s)); +// break; +// } +// case WARNING : { +// info.setLastOperationStatus(LifecycleInformation.Status.WARNING); +// matRep.getMessages().forEach(s -> info.addWarningMessage(s)); +// break; +// } +// } +// +// } +// break; +// case STEPS.REJECT:{ +// if(!request.getDocument().getLifecycleInformation().getPhase().equals(Phases.PENDING_APPROVAL)) +// throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); +// // TODO OPTIONAL Notification +// +// info.setPhase(Phases.PENDING_APPROVAL); +// if(request.getCallParameters()!=null&&request.getCallParameters().containsKey(PARAMETERS.NOTES)) +// info.setNotes(request.getCallParameters().getString(PARAMETERS.NOTES)); +// break; +// } +// +// case STEPS.APPROVE:{ +// // Index-published +// if(!request.getDocument().getLifecycleInformation().getPhase() +// .equals(Phases.PENDING_APPROVAL)) +// throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); +// +// index(report,indexerPlugin); +// break; +// } +// +// default: +// throw new StepException("Invalid Step " + request.getStep()); +// } +// }catch (StepException e){ +// throw e; +// }catch (Throwable t){ +// log.error("Unable to perform step "+request.getStep(),t); +// String msg = "Unable to execute Step "+request.getStep()+". Error was "+t.getMessage(); +// report.setStatus(Report.Status.ERROR); +// report.putMessage(msg); +// info.setLastOperationStatus(LifecycleInformation.Status.ERROR); +// info.addErrorMessage(msg); +// } +// return report; +// } @Override public Configuration getCurrentConfiguration(BaseRequest req) throws ConfigurationException { Configuration toReturn = super.getCurrentConfiguration(req); - toReturn.setIndexes(new ArrayList<>()); IndexerPluginInterface indexerPlugin; indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); BaseRequest indexRequest = new BaseRequest(req.getUseCaseDescriptor(),req.getCaller(),req.getContext()); - // Info on Public index - try { - indexRequest.setCallParameters(getPublicIndexParams(req)); - Index publicIndex = indexerPlugin.getIndex(indexRequest); - publicIndex.put("flag", "public"); - toReturn.getIndexes().add(publicIndex); - }catch(ConfigurationException e){ - toReturn.addErrorMessage("Unable to gather information on public GIS Centroids Index : "+e.getMessage()); - log.error("Unable to gather information on public GIS Centroids Index",e); - } // Info on internal_index try { indexRequest.setCallParameters(getInternalIndexParams(req)); @@ -106,7 +231,6 @@ public class Default3PhaseManager extends AbstractLifeCycleManager implements Li return toReturn; } - private Document getInternalIndexParams(BaseRequest req){ Document callParameters = new Document(); @@ -114,210 +238,4 @@ public class Default3PhaseManager extends AbstractLifeCycleManager implements Li callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+"_internal_"+req.getContext().getName()+"_centroids")); return callParameters; } - - private Document getPublicIndexParams(BaseRequest req){ - Document callParameters = new Document(); - callParameters.put("workspace",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName())); - callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName()+"_centroids")); - return callParameters; - } - - - @Override - public EventExecutionReport onEvent(EventExecutionRequest request) throws EventException, InvalidPluginRequestException { - EventExecutionReport report=super.onEvent(request); - TriggeredEvents info = report.getToSetLifecycleInformation().getLastEvent(); - try { - switch(request.getEvent()){ - case EventExecutionRequest.Events.ON_INIT_DOCUMENT: - // Set Defaults as for on update - case EventExecutionRequest.Events.ON_UPDATE_DOCUMENT: { - log.debug("Setting default values.."); - report=setDefault(report); - break; - } - case EventExecutionRequest.Events.ON_DELETE_DOCUMENT: { - //DELETE ALL - break; - } - default: throw new EventException("Unexpected Event "+request.getEvent()); - } - }catch (EventException e){ - throw e; - }catch (Throwable t){ - log.error("Unable to execute on event "+request.getEvent(),t); - String msg = "Unable to execute on event "+request.getEvent()+". Error was "+t.getMessage(); - info.setLastOperationStatus(LifecycleInformation.Status.ERROR); - info.addErrorMessage(msg); - report.setStatus(Report.Status.ERROR); - report.putMessage(msg); - } - return report; - } - - - - @Override - public StepExecutionReport performStep(StepExecutionRequest request) throws StepException, InvalidPluginRequestException, InvalidProfileException, ConfigurationException, InsufficientPrivileges { - StepExecutionReport report = super.performStep(request); - LifecycleInformation info=report.getToSetLifecycleInformation(); - try { - if(!canInvokeStep(request.getStep(),request.getCaller(),getConfigurationFromProfile(request.getUseCaseDescriptor()))) - throw new InsufficientPrivileges("Insufficient privileges for executing step "+request.getStep()); - - MaterializationPlugin plugin; - IndexerPluginInterface indexerPlugin; - plugin= (MaterializationPlugin) pluginManager.getById("SDI-Default-Materializer"); - indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); - - - - switch (request.getStep()) { - case STEPS.SUBMIT:{ - //TODO validation - - if(!request.getDocument().getLifecycleInformation().getPhase().equals(LifecycleInformation.DRAFT_PHASE)) - throw new StepException("Document is not in "+LifecycleInformation.DRAFT_PHASE+" phase"); - //Materialize layers - MaterializationRequest matReq = new MaterializationRequest(request.getUseCaseDescriptor(),request.getCaller(), request.getContext(), request.getDocument()); - - matReq.setDocument(request.getDocument()); - matReq.setUseCaseDescriptor(request.getUseCaseDescriptor()); - Document params = new Document(); - String workspace = request.getUseCaseDescriptor().getId() + request.getContext().getId(); - params.put("workspace", Files.fixFilename(workspace)); - - - matReq.setCallParameters(params); - MaterializationReport matRep = plugin.materialize(matReq); - - - report.setResultingDocument(matRep.getResultingDocument()); - - - switch(matRep.getStatus()){ - case OK : { - index(report,indexerPlugin); - //TODO Optional Notifications - break; - } - case ERROR : { - info.setLastOperationStatus(LifecycleInformation.Status.ERROR); - matRep.getMessages().forEach(s -> info.addErrorMessage(s)); - break; - } - case WARNING : { - info.setLastOperationStatus(LifecycleInformation.Status.WARNING); - matRep.getMessages().forEach(s -> info.addWarningMessage(s)); - break; - } - } - - } - break; - case STEPS.REJECT:{ - if(!request.getDocument().getLifecycleInformation().getPhase().equals(Phases.PENDING_APPROVAL)) - throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); - // TODO OPTIONAL Notification - - info.setPhase(Phases.PENDING_APPROVAL); - if(request.getCallParameters()!=null&&request.getCallParameters().containsKey(PARAMETERS.NOTES)) - info.setNotes(request.getCallParameters().getString(PARAMETERS.NOTES)); - break; - } - - case STEPS.APPROVE:{ - // Index-published - if(!request.getDocument().getLifecycleInformation().getPhase() - .equals(Phases.PENDING_APPROVAL)) - throw new StepException("Document is not in "+Phases.PENDING_APPROVAL+" phase"); - - index(report,indexerPlugin); - break; - } - - default: - throw new StepException("Invalid Step " + request.getStep()); - } - }catch (StepException e){ - throw e; - }catch (Throwable t){ - log.error("Unable to perform step "+request.getStep(),t); - String msg = "Unable to execute Step "+request.getStep()+". Error was "+t.getMessage(); - report.setStatus(Report.Status.ERROR); - report.putMessage(msg); - info.setLastOperationStatus(LifecycleInformation.Status.ERROR); - info.addErrorMessage(msg); - } - return report; - } - - - - private IndexDocumentReport index(StepExecutionReport report,IndexerPluginInterface indexer) throws InvalidPluginRequestException { - StepExecutionRequest request = report.getTheRequest(); - LifecycleInformation info=report.getToSetLifecycleInformation(); - IndexDocumentRequest indexRequest = new IndexDocumentRequest( - request.getUseCaseDescriptor(),request.getCaller(), request.getContext(),request.getDocument()); - - - indexRequest.setCallParameters(new Document()); - if(request.getStep().equals(STEPS.APPROVE)) { - // public index - indexRequest.getCallParameters().putAll(getPublicIndexParams(request)); - } - else { - // private index - indexRequest.getCallParameters().putAll(getPublicIndexParams(request)); - } - IndexDocumentReport indexReport = indexer.index(indexRequest); - - - switch(indexReport.getStatus()){ - case OK : { - if(request.getStep().equals(STEPS.APPROVE)) { - // public index - info.setPhase(Phases.PUBLISHED); - } - else { - // private index - info.setPhase(Phases.PENDING_APPROVAL); - } - report.setToSetSpatialReference(indexReport.getToSetSpatialReference()); - break; - } - case ERROR : { - info.setLastOperationStatus(LifecycleInformation.Status.ERROR); - indexReport.getMessages().forEach(s -> info.addErrorMessage(s)); - break; - } - case WARNING : { - info.setLastOperationStatus(LifecycleInformation.Status.WARNING); - indexReport.getMessages().forEach(s -> info.addWarningMessage(s)); - break; - } - } - - return indexReport; - - } - - - - - /** - * Override this method for programmatic default values management - * - * @param currentReport - * @return - */ - public EventExecutionReport setDefault(EventExecutionReport currentReport){ - // Default implementation is no op - return currentReport; - } - - @Override - public PluginDescriptor getDescriptor() { - return DESCRIPTOR; - } } diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedEventManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedEventManager.java new file mode 100644 index 0000000..99e7985 --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedEventManager.java @@ -0,0 +1,15 @@ +package org.gcube.application.cms.plugins.implementations; + +import lombok.extern.slf4j.Slf4j; +import org.gcube.application.cms.plugins.faults.EventException; +import org.gcube.application.cms.plugins.faults.InvalidPluginRequestException; +import org.gcube.application.cms.plugins.reports.EventExecutionReport; +import org.gcube.application.cms.plugins.requests.EventExecutionRequest; + +import javax.ws.rs.WebApplicationException; + +@Slf4j +public abstract class GuardedEventManager extends GuardedExecution{ + + +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedExecution.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedExecution.java new file mode 100644 index 0000000..82126bb --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedExecution.java @@ -0,0 +1,35 @@ +package org.gcube.application.cms.plugins.implementations; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.gcube.application.cms.plugins.reports.DocumentHandlingReport; +import org.gcube.application.cms.plugins.requests.BaseExecutionRequest; + + +@Slf4j +public abstract class GuardedExecution { + + @Getter + protected T result = null; + + + protected T theReport; + + public T execute() throws Exception { + log.trace("Executing {} ",theReport.getTheRequest()); + if(theReport==null) throw new RuntimeException("Unexpected state : request cannot be null"); + result = run(); + log.trace("Report is {} ",theReport); + return result; + } + + public T getResult() { + return result; + } + protected abstract T run() throws Exception; + + public GuardedExecution setTheReport(T theReport) { + this.theReport = theReport; + return this; + } +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedStepExecution.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedStepExecution.java new file mode 100644 index 0000000..4294c1d --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/GuardedStepExecution.java @@ -0,0 +1,7 @@ +package org.gcube.application.cms.plugins.implementations; + +import org.gcube.application.cms.plugins.reports.StepExecutionReport; +import org.gcube.application.cms.plugins.requests.StepExecutionRequest; + +public abstract class GuardedStepExecution extends GuardedExecution{ +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/RoleManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/RoleManager.java new file mode 100644 index 0000000..09f548a --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/RoleManager.java @@ -0,0 +1,60 @@ +package org.gcube.application.cms.plugins.implementations; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.gcube.application.cms.serialization.Serialization; +import org.gcube.application.geoportal.common.model.document.accounting.User; +import org.gcube.application.geoportal.common.model.rest.ConfigurationException; +import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration; + +import javax.xml.bind.annotation.XmlRootElement; +import java.util.HashMap; +import java.util.List; + +@Slf4j +public class RoleManager { + + HashMap accessMap=new HashMap<>(); + + + public RoleManager(HandlerDeclaration config) throws ConfigurationException { + List l =config.getConfiguration().get("step_access", List.class); + if(l==null|| l.isEmpty()) throw new ConfigurationException("Missing Role management in UCD"); + for (Object o : l) { + StepAccess a= Serialization.convert(o, StepAccess.class); + accessMap.put(a.getStepId(),a); + } + } + + public boolean canInvokeStep(String stepID,User u) throws ConfigurationException { + if(!accessMap.containsKey(stepID)) throw new ConfigurationException("Missing step "+stepID+" access definition"); + + for (String role : accessMap.get(stepID).getRoles()) + if (u.getRoles().contains(role)) + return true; + + return false; + } + + @XmlRootElement + @AllArgsConstructor + @Getter + @Setter + @ToString(callSuper = true) + public static class StepAccess { + + public static final String STEP="STEP"; + public static final String ROLES="roles"; + + + @JsonProperty(STEP) + private String stepId; + + @JsonProperty(ROLES) + private List roles; + } +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/SimpleLifeCycleManager.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/SimpleLifeCycleManager.java new file mode 100644 index 0000000..7d61074 --- /dev/null +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/implementations/SimpleLifeCycleManager.java @@ -0,0 +1,238 @@ +package org.gcube.application.cms.plugins.implementations; + +import lombok.extern.slf4j.Slf4j; +import org.bson.Document; +import org.gcube.application.cms.plugins.IndexerPluginInterface; +import org.gcube.application.cms.plugins.LifecycleManager; +import org.gcube.application.cms.plugins.MaterializationPlugin; +import org.gcube.application.cms.plugins.faults.*; +import org.gcube.application.cms.plugins.reports.*; +import org.gcube.application.cms.plugins.requests.*; +import org.gcube.application.geoportal.common.model.JSONPathWrapper; +import org.gcube.application.geoportal.common.model.configuration.Configuration; +import org.gcube.application.geoportal.common.model.configuration.Index; +import org.gcube.application.geoportal.common.model.document.filesets.RegisteredFileSet; +import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation; +import org.gcube.application.geoportal.common.model.rest.ConfigurationException; +import org.gcube.application.geoportal.common.utils.Files; + +import java.util.ArrayList; + +@Slf4j +public class SimpleLifeCycleManager extends AbstractLifeCycleManager implements LifecycleManager { + + @Override + public Configuration getCurrentConfiguration(BaseRequest req) throws ConfigurationException { + Configuration toReturn = super.getCurrentConfiguration(req); + toReturn.setIndexes(new ArrayList<>()); + + IndexerPluginInterface indexerPlugin; + indexerPlugin = (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); + BaseRequest indexRequest = new BaseRequest(req.getUseCaseDescriptor(),req.getCaller(),req.getContext()); + + // Info on Public index + try { + indexRequest.setCallParameters(getPublicIndexParams(req)); + Index publicIndex = indexerPlugin.getIndex(indexRequest); + publicIndex.put("flag", "public"); + toReturn.getIndexes().add(publicIndex); + }catch(ConfigurationException e){ + toReturn.addErrorMessage("Unable to gather information on public GIS Centroids Index : "+e.getMessage()); + log.error("Unable to gather information on public GIS Centroids Index",e); + } + + return toReturn; + } + + + protected Document getPublicIndexParams(BaseRequest req){ + Document callParameters = new Document(); + callParameters.put("workspace",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName())); + callParameters.put("indexName",Files.fixFilename(req.getUseCaseDescriptor().getId()+req.getContext().getName()+"_centroids")); + return callParameters; + } + + + protected Document getMaterializationParameters(BaseRequest request){ + Document params = new Document(); + String workspace = request.getUseCaseDescriptor().getId() + request.getContext().getId(); + params.put("workspace", Files.fixFilename(workspace)); + return params; + } + + + @Override + protected void registerSteps() { + setStep("PUBLISH", new GuardedStepExecution() { + @Override + protected StepExecutionReport run() throws Exception { + if(!theReport.getTheRequest().getDocument().getLifecycleInformation().getPhase().equals(LifecycleInformation.CommonPhases.DRAFT_PHASE)) + throw new StepException("Document is not in "+LifecycleInformation.CommonPhases.DRAFT_PHASE+" phase"); + + // Materialize + theReport = materializeDocument(theReport, getMaterializer(), + getMaterializationParameters(theReport.getTheRequest())); + + if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)){ + // Index + theReport = index(theReport,getIndexer(), + getPublicIndexParams(theReport.getTheRequest())); + // setPhase + if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) + theReport.getToSetLifecycleInformation().setPhase("PUBLISHED"); + } + return theReport; + } + }); + } + + protected void blockNonDraft(EventExecutionReport report) throws InvalidPluginRequestException { + Boolean force = Boolean.parseBoolean(report.getTheRequest().getMandatory("force")); + if(!report.getTheRequest().getDocument().getLifecycleInformation().getPhase().equals(LifecycleInformation.CommonPhases.DRAFT_PHASE) && ! force) + throw new InvalidPluginRequestException("Document is not in "+LifecycleInformation.CommonPhases.DRAFT_PHASE+" phase"); + } + + @Override + protected EventExecutionReport onInitDocument(EventExecutionReport report) throws InvalidPluginRequestException { + blockNonDraft(report); + return super.onInitDocument(report); + } + + @Override + protected EventExecutionReport onUpdateDocument(EventExecutionReport report) { + return super.onUpdateDocument(report); + } + + @Override + protected EventExecutionReport onDeleteDocument(EventExecutionReport report) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException { + // dematerialize all + blockNonDraft(report); + JSONPathWrapper wrapper = new JSONPathWrapper(report.getTheRequest().getDocument().getTheDocument().toJson()); + for (String s : wrapper.getMatchingPaths("..*[?(@." + RegisteredFileSet.PAYLOADS + ")]")){ + log.info("Requesting dematerialization for {} ",s); + report = deMaterialize(report,getMaterializer(),new Document("fileSetPath",s)); + if(!report.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) + break; + } + if(report.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) { + report = deIndex(report,getIndexer(),getPublicIndexParams(report.getTheRequest())); + } + return report; + } + + @Override + protected EventExecutionReport onDeleteFileSet(EventExecutionReport theReport) throws ConfigurationException, InvalidPluginRequestException, MaterializationException, EventException { + // dematerialize selected + blockNonDraft(theReport); + deMaterialize(theReport,getMaterializer(), + theReport.getTheRequest().getCallParameters()); + // de index + if(theReport.getToSetLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) + deIndex(theReport,getIndexer(),getPublicIndexParams(theReport.getTheRequest())); + return theReport; + } + + + @Override + protected void registerEvents() { + super.registerEvents(); + // register events + setEvent(EventExecutionRequest.Events.ON_DELETE_DOCUMENT, new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + return onDeleteDocument(theReport); + } + }); + setEvent(EventExecutionRequest.Events.ON_DELETE_FILESET, new GuardedEventManager() { + @Override + protected EventExecutionReport run() throws Exception { + return onDeleteFileSet(theReport); + } + }); + } + + + protected IndexerPluginInterface getIndexer() throws ConfigurationException { + return (IndexerPluginInterface) pluginManager.getById("SDI-Indexer-Plugin"); + } + + protected MaterializationPlugin getMaterializer() throws ConfigurationException { + return (MaterializationPlugin) pluginManager.getById("SDI-Default-Materializer"); + } + + protected T deIndex(T report, IndexerPluginInterface indexer, Document parameters) throws InvalidPluginRequestException { + BaseExecutionRequest request = report.getTheRequest(); + IndexDocumentRequest indexRequest = new IndexDocumentRequest( + request.getUseCaseDescriptor(),request.getCaller(), request.getContext(),request.getDocument()); + + indexRequest.setCallParameters(parameters); + IndexDocumentReport indexReport = indexer.deindex(indexRequest); + + return handleReport(indexReport,report); + + } + + protected T deMaterialize(T report, MaterializationPlugin plugin, Document parameters) throws InvalidPluginRequestException, MaterializationException { + BaseExecutionRequest request = report.getTheRequest(); + MaterializationRequest matReq = + new MaterializationRequest(request.getUseCaseDescriptor(),request.getCaller(), request.getContext(), request.getDocument()); + + Document params = new Document(); + String workspace = request.getUseCaseDescriptor().getId() + request.getContext().getId(); + params.put("workspace", Files.fixFilename(workspace)); + + matReq.setCallParameters(params); + MaterializationReport matRep = plugin.dematerialize(matReq); + + return handleReport(matRep,report); + } + + protected T index(T report, IndexerPluginInterface indexer, Document parameters) throws InvalidPluginRequestException { + BaseExecutionRequest request = report.getTheRequest(); + IndexDocumentRequest indexRequest = new IndexDocumentRequest( + request.getUseCaseDescriptor(),request.getCaller(), request.getContext(),request.getDocument()); + + indexRequest.setCallParameters(parameters); + IndexDocumentReport indexReport = indexer.index(indexRequest); + + return handleReport(indexReport,report); + + } + + protected T materializeDocument(T report,MaterializationPlugin plugin,Document parameters) throws InvalidPluginRequestException, MaterializationException { + BaseExecutionRequest request = report.getTheRequest(); + MaterializationRequest matReq = + new MaterializationRequest(request.getUseCaseDescriptor(),request.getCaller(), request.getContext(), request.getDocument()); + + matReq.setCallParameters(parameters); + MaterializationReport matRep = plugin.materialize(matReq); + + return handleReport(matRep,report); + } + + + private T handleReport(DocumentHandlingReport toHandle, T toUpdate){ + toUpdate.setResultingDocument(toHandle.getResultingDocument()); + LifecycleInformation info = toUpdate.getToSetLifecycleInformation(); + switch(toHandle.getStatus()){ + case OK : { + info.setLastOperationStatus(LifecycleInformation.Status.OK); + if(toHandle instanceof IndexDocumentReport) + toUpdate.setToSetSpatialReference(((IndexDocumentReport)toHandle).getToSetSpatialReference()); + break; + } + case ERROR : { + info.setLastOperationStatus(LifecycleInformation.Status.ERROR); + toHandle.getMessages().forEach(s -> info.addErrorMessage(s)); + break; + } + case WARNING : { + info.setLastOperationStatus(LifecycleInformation.Status.WARNING); + toHandle.getMessages().forEach(s -> info.addWarningMessage(s)); + break; + } + } + return toUpdate; + } + +} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java deleted file mode 100644 index 6b3dc88..0000000 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/model/StepAccess.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.gcube.application.cms.plugins.model; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -import javax.xml.bind.annotation.XmlRootElement; -import java.util.List; - -@XmlRootElement -@AllArgsConstructor -@Getter -@Setter -@ToString(callSuper = true) -public class StepAccess { - - public static final String STEP="STEP"; - public static final String ROLES="roles"; - - - @JsonProperty(STEP) - private String stepId; - - @JsonProperty(ROLES) - private List roles; -} diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/DocumentHandlingReport.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/DocumentHandlingReport.java index 266f856..d5356c8 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/DocumentHandlingReport.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/DocumentHandlingReport.java @@ -10,6 +10,7 @@ import org.gcube.application.cms.plugins.faults.PluginExecutionException; import org.gcube.application.cms.plugins.requests.BaseExecutionRequest; import org.gcube.application.geoportal.common.model.document.Project; import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation; +import org.gcube.application.geoportal.common.model.document.temporal.TemporalReference; @Data @@ -23,6 +24,10 @@ public class DocumentHandlingReport extends Repo LifecycleInformation toSetLifecycleInformation; + TemporalReference toSetTemporalReference; + Document toSetSpatialReference; + + public DocumentHandlingReport(@NonNull T theRequest) throws InvalidPluginRequestException { theRequest.validate(); this.theRequest = theRequest; @@ -71,6 +76,8 @@ public class DocumentHandlingReport extends Repo break; } } + if(toSetSpatialReference != null) toReturn.setSpatialReference(toSetSpatialReference); + if(toSetTemporalReference != null) toReturn.setTemporalReference(toSetTemporalReference); return toReturn; } } diff --git a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/StepExecutionReport.java b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/StepExecutionReport.java index c405a72..3cd1931 100644 --- a/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/StepExecutionReport.java +++ b/cms-plugin-framework/src/main/java/org/gcube/application/cms/plugins/reports/StepExecutionReport.java @@ -30,8 +30,6 @@ public class StepExecutionReport extends DocumentHandlingReport cascadeSteps; - public TemporalReference toSetTemporalReference; - public Document toSetSpatialReference; public StepExecutionReport addToTriggerEvent(EventExecutionRequest req){ @@ -50,8 +48,7 @@ public class StepExecutionReport extends DocumentHandlingReport { // delete - public void delete(String id,boolean force) throws DeletionException, InvalidUserRoleException, ProjectLockedException, ProjectNotFoundException, UnauthorizedAccess, JsonProcessingException; + public void delete(String id,boolean force) throws DeletionException, InvalidUserRoleException, ProjectLockedException, ProjectNotFoundException, UnauthorizedAccess, JsonProcessingException, InvalidLockException; // get By ID diff --git a/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java b/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java index 1cce640..1a6fc60 100644 --- a/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java +++ b/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java @@ -18,6 +18,7 @@ import org.gcube.application.cms.plugins.faults.InsufficientPrivileges; import org.gcube.application.cms.plugins.faults.StepException; import org.gcube.application.cms.plugins.model.PluginDescriptor; import org.gcube.application.cms.plugins.reports.DocumentHandlingReport; +import org.gcube.application.cms.plugins.reports.EventExecutionReport; import org.gcube.application.cms.plugins.reports.StepExecutionReport; import org.gcube.application.cms.plugins.requests.BaseRequest; import org.gcube.application.cms.plugins.requests.EventExecutionRequest; @@ -218,7 +219,7 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< LifecycleInformation draftInfo=new LifecycleInformation().cleanState(); - draftInfo.setPhase(LifecycleInformation.DRAFT_PHASE); + draftInfo.setPhase(LifecycleInformation.CommonPhases.DRAFT_PHASE); draftInfo.setLastOperationStatus(LifecycleInformation.Status.OK); toRegister.setLifecycleInformation(draftInfo); @@ -274,39 +275,39 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< @Override - public void delete(String id,boolean force) throws DeletionException, InvalidUserRoleException, ProjectLockedException, ProjectNotFoundException, UnauthorizedAccess, JsonProcessingException { - log.debug("Deleting by ID {}, force {}",id,force); + public void delete(String id,boolean force) throws DeletionException, InvalidUserRoleException, ProjectLockedException, ProjectNotFoundException, UnauthorizedAccess, JsonProcessingException, InvalidLockException { + log.info("Deleting by ID {}, force {}",id,force); Project doc =lock(id,"Deletion { force : "+force+"}"); - + try { User u = UserUtils.getCurrent().asInfo().getUser(); - final DataAccessPolicy policy = useCaseDescriptor.getMatching(u); - log.info("Registering Fileset for {} [{}] , policy for {} is {} ",id,useCaseDescriptor.getId(),u,policy); - // NB cannot check ownership on returned values, must specify filter - if(policy == null) { - log.warn("No policy found for {}. Returning empty ", u); - throw new InvalidUserRoleException("No policy defined for current user roles " + u.getRoles()); + final DataAccessPolicy policy = useCaseDescriptor.getMatching(u); + log.debug("Delete project {} [{}] , policy for {} is {} ", id, useCaseDescriptor.getId(), u, policy); + if (policy == null) throw new InvalidUserRoleException("No policy defined for current user roles " + u.getRoles()); + if (!policy.canWrite(doc, u)) throw new UnauthorizedAccess("No edit rights on project " + id); + + doc = triggerEvent(doc, EventExecutionRequest.Events.ON_DELETE_DOCUMENT, new Document("force", force)); + //Only continue deleting if event was ok + if(doc.getLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) { + try { + WorkspaceManager ws = new WorkspaceManager(); + // Get All registered Filesets with payloads + JSONPathWrapper wrapper = new JSONPathWrapper(useCaseDescriptor.getSchema().toJson()); + for (Object obj : wrapper.getByPath("..*[?(@." + RegisteredFileSet.PAYLOADS + ")]")) { + Document fs = Serialization.asDocument(obj); + log.debug("Deleting {}",obj); + String folderId = fs.getString(RegisteredFileSet.FOLDER_ID); + ws.deleteItem(folderId); + } + }finally { + if(force) super.deleteDoc(asId(id)); + } } - if(!policy.canWrite(doc,u)) throw new UnauthorizedAccess("No edit rights on project "+id); - - // TODO INVOKE LIFECYCLE - - //if(!force&&isPublished(id)) throw new Exception("Cannot delete published documents. Unpublish it or use force = true"); - - try{ - // TODO CHECK PHASE AND STATUS - // DEINDEX - // DEMATERIALIZE - // DELETE CONTENT - // DELETE ENTRY - throw new DeletionException("IMPLEMENT THIS"); - // delete(asId(id), getCollectionName()); - }catch(DeletionException e) { - //storing updated - partially deleted - // concessione=onUpdate(concessione); - // replace(asDocumentWithId(concessione), collectionName); - throw e; - } - + } catch (ConfigurationException | StorageHubException e) { + log.error("Exception while trying to delete {} [UCID {}]",id,useCaseDescriptor.getId()); + throw new DeletionException("Unable to contact Storage ",e); + } finally{ + if(doc!=null) unlockAndUpdate(doc); + } } @Override @@ -512,9 +513,11 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< throw new WebApplicationException("Cannot replace repeatable field " + request.getFieldDefinitionPath() + ".", Response.Status.BAD_REQUEST); // DELETE EXISTING AND PUT RegisteredFileSet toDelete = Serialization.convert(parent.get(request.getFieldName()), RegisteredFileSet.class); - if (!(toDelete == null) && !(toDelete.isEmpty())) - deleteFileSetRoutine(toDelete, false, ws); + if (!(toDelete == null) && !(toDelete.isEmpty())) { + String path = parentMatchingPath+"."+request.getFieldName(); + deleteFileSetRoutine(doc, false, path); + } RegisteredFileSet fs = prepareRegisteredFileSet(doc.getInfo(), doc.getId(), useCaseDescriptor.getId(), request.getAttributes(), files, storage, ws); log.debug("Registered Fileset for [ID {} useCaseDescriptor {}] is {} ", fs, doc.getId(), doc.getProfileID()); docWrapper.putElement(parentMatchingPath, request.getFieldName(), fs); @@ -564,44 +567,59 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< @Override public Project deleteFileSet(String id, String path, Boolean force) throws ConfigurationException, StorageHubException, JsonProcessingException, DeletionException, EventException, ProjectLockedException, ProjectNotFoundException, InvalidLockException, InvalidUserRoleException, UnauthorizedAccess { log.info("Deleting Fileset for {} [useCaseDescriptor ID {}], at {} [force {} ]",id, useCaseDescriptor.getId(),path,force); - Project doc = lock(id,"Fileset Deletion"); - try { - - User u = UserUtils.getCurrent().asInfo().getUser(); final DataAccessPolicy policy = useCaseDescriptor.getMatching(u); - log.info("Registering Fileset for {} [{}] , policy for {} is {} ",id,useCaseDescriptor.getId(),u,policy); + log.debug("Deleting Fileset for {} [{}] , policy for {} is {} ",doc.getId(),useCaseDescriptor.getId(),u,policy); // NB cannot check ownership on returned values, must specify filter if(policy == null) { log.warn("No policy found for {}. Returning empty ", u); throw new InvalidUserRoleException("No policy defined for current user roles " + u.getRoles()); } - if(!policy.canWrite(doc,u)) throw new UnauthorizedAccess("No edit rights on project "+id); - + if(!policy.canWrite(doc,u)) throw new UnauthorizedAccess("No edit rights on project "+doc.getId()); doc.getLifecycleInformation().cleanState(); doc.getLifecycleInformation().cleanState().setLastOperationStatus(LifecycleInformation.Status.OK); - - JSONPathWrapper wrapper = new JSONPathWrapper(doc.getTheDocument().toJson()); - List matchingPaths = wrapper.getMatchingPaths(path); - if (matchingPaths.isEmpty()) - throw new WebApplicationException("No Registered FileSet found at " + path, Response.Status.BAD_REQUEST); - if (matchingPaths.size() > 1) - throw new WebApplicationException("Multiple Fileset (" + matchingPaths.size() + ") matching " + path, Response.Status.BAD_REQUEST); - RegisteredFileSet fs = Serialization.convert(wrapper.getByPath(path), RegisteredFileSet.class); - log.debug("Going to delete {}", fs); - deleteFileSetRoutine(fs, force, new WorkspaceManager()); - log.debug("Removing FS from document [ID : ] by path {}", id, path); - wrapper.setElement(path, null); + doc= deleteFileSetRoutine(doc,force,path); doc = onUpdate(doc); + return doc; }finally { return unlockAndUpdate(doc); } } + + private Project deleteFileSetRoutine(Project doc,Boolean force, String path) throws ConfigurationException, StorageHubException { + JSONPathWrapper wrapper = new JSONPathWrapper(doc.getTheDocument().toJson()); + List matchingPaths = wrapper.getMatchingPaths(path); + if (matchingPaths.isEmpty()) + throw new WebApplicationException("No Registered FileSet found at " + path, Response.Status.BAD_REQUEST); + if (matchingPaths.size() > 1) + throw new WebApplicationException("Multiple Fileset (" + matchingPaths.size() + ") matching " + path, Response.Status.BAD_REQUEST); + + RegisteredFileSet fs = Serialization.convert(wrapper.getByPath(path), RegisteredFileSet.class); + log.debug("Going to delete {}", fs); + + doc = triggerEvent(doc,EventExecutionRequest.Events.ON_DELETE_FILESET,new Document("force",force).append("path",path)); + + // Actually delete only if event was ok + if(doc.getLifecycleInformation().getLastOperationStatus().equals(LifecycleInformation.Status.OK)) { + // Delete from storage + if(fs.getFolderId()!=null) { + log.info("Deleting Fileset Folder ID {} ",fs.getFolderId()); + new WorkspaceManager().deleteItem(fs.getFolderId()); + } + log.debug("Removing FS from document [ID : ] by path {}", doc.getId(), path); + // Delete from document + wrapper.setElement(path, null); + } + return doc; + } + + + @Override public Configuration getConfiguration() throws ConfigurationException{ log.debug("Asking configuration for {} in {} ", useCaseDescriptor.getId(), UserUtils.getCurrent().getContext()); @@ -653,6 +671,8 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< } + + private Project step(Project theDocument, String step, Document callParameters) throws InsufficientPrivileges, ConfigurationException { try { log.info("[UseCaseDescriptor {}] Invoking Step {} on {}", useCaseDescriptor.getId(), step, getManager().getDescriptor()); @@ -768,19 +788,6 @@ public class ProfiledMongoManager extends MongoManager implements MongoManagerI< } - private static void deleteFileSetRoutine(RegisteredFileSet fs, Boolean force, WorkspaceManager ws) throws DeletionException, StorageHubException { - log.debug("Deleting Registered FS {}"); - if(fs.getMaterializations()!=null && !fs.getMaterializations().isEmpty()){ - if(!force) throw new DeletionException("Fileset (uuid "+fs.getUUID()+") already materialized. Use force = true"); - else throw new RuntimeException("Implement this"); - // TODO manager force deletion - // NB handlers for materialization types - } - log.trace("FileSet ID {} : deleting ws folder {}",fs.getUUID(),fs.getFolderId()); - if(fs.getPayloads()!=null) - ws.deleteItem(fs.getFolderId()); - } - private static Field getFieldDefinition(UseCaseDescriptor useCaseDescriptor, String fieldPath)throws WebApplicationException{ JSONPathWrapper schemaWrapper= new JSONPathWrapper(useCaseDescriptor.getSchema().toJson()); diff --git a/geoportal-service/src/test/java/org/gcube/application/geoportal/service/engine/mongo/LockTests.java b/geoportal-service/src/test/java/org/gcube/application/geoportal/service/engine/mongo/LockTests.java index 52104b0..891918f 100644 --- a/geoportal-service/src/test/java/org/gcube/application/geoportal/service/engine/mongo/LockTests.java +++ b/geoportal-service/src/test/java/org/gcube/application/geoportal/service/engine/mongo/LockTests.java @@ -77,7 +77,7 @@ public class LockTests extends BasicServiceTestUnit { } @Test - public void testUnlock() throws StepException, EventException, IOException, ProjectNotFoundException, ProjectLockedException, InvalidLockException, DeletionException, ConfigurationException, StorageHubException, StorageException { + public void testUnlock() throws StepException, EventException, IOException, ProjectNotFoundException, ProjectLockedException, InvalidLockException, DeletionException, ConfigurationException, StorageHubException, StorageException, InvalidUserRoleException, UnauthorizedAccess { assumeTrue(GCubeTest.isTestInfrastructureEnabled()); MongoManagerI managerInterface = manager; // create @@ -107,7 +107,7 @@ public class LockTests extends BasicServiceTestUnit { checkIsLockCleaned(p.getId()); } - private void checkIsLockCleaned(String id) throws ProjectNotFoundException { + private void checkIsLockCleaned(String id) throws ProjectNotFoundException, InvalidUserRoleException, UnauthorizedAccess { assertNull(manager.getByID(id).getLock()); } } diff --git a/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java b/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java index 0dd01b4..3586ee8 100644 --- a/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java +++ b/sdi-plugins/src/main/java/org/gcube/application/cms/sdi/plugins/SDIMaterializerPlugin.java @@ -153,6 +153,11 @@ public class SDIMaterializerPlugin extends AbstractPlugin implements Materializa } } + @Override + public MaterializationReport dematerialize(MaterializationRequest request) throws MaterializationException, InvalidPluginRequestException { + return null; + } + private static final PluginDescriptor DESCRIPTOR=new PluginDescriptor("SDI-Default-Materializer", PluginDescriptor.BaseTypes.MATERIALIZER); static { DESCRIPTOR.setDescription("SDI Materializer. " +