package org.gcube.application.geoportal.service.rest; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import org.bson.Document; import org.gcube.application.cms.implementations.ImplementationProvider; import org.gcube.application.cms.implementations.utils.UserUtils; import org.gcube.application.cms.plugins.events.EventManager; import org.gcube.application.cms.plugins.events.EventManager.Event; import org.gcube.application.cms.plugins.events.ItemObserved; import org.gcube.application.cms.plugins.events.ItemObserved.OPTIONAL_FIELD; import org.gcube.application.cms.serialization.Serialization; import org.gcube.application.geoportal.common.model.configuration.Configuration; import org.gcube.application.geoportal.common.model.document.Project; import org.gcube.application.geoportal.common.model.document.access.Access; import org.gcube.application.geoportal.common.model.document.accounting.AccountingInfo; import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation; import org.gcube.application.geoportal.common.model.document.lifecycle.LifecycleInformation.Status; import org.gcube.application.geoportal.common.model.rest.ConfigurationException; import org.gcube.application.geoportal.common.model.rest.QueryRequest; import org.gcube.application.geoportal.common.model.rest.RegisterFileSetRequest; import org.gcube.application.geoportal.common.model.rest.PerformStepRequest; import org.gcube.application.geoportal.common.rest.InterfaceConstants; import org.gcube.application.geoportal.service.engine.mongo.ProfiledMongoManager; import org.gcube.application.geoportal.service.engine.providers.ConfigurationCache; import org.gcube.application.geoportal.service.engine.providers.ProjectAccessImpl; import org.gcube.application.geoportal.service.http.PATCH; import com.webcohesion.enunciate.metadata.rs.RequestHeader; import com.webcohesion.enunciate.metadata.rs.RequestHeaders; import lombok.extern.slf4j.Slf4j; /** * The Class ProfiledDocuments. * * @author created by Fabio Sinibaldi * @author new manager/developer and mantainer - Francesco Mangiacrapa at * ISTI-CNR francesco.mangiacrapa@isti.cnr.it * * Apr 18, 2023 */ @Path(InterfaceConstants.Methods.PROJECTS + "/{" + InterfaceConstants.Parameters.UCID + "}") @Slf4j @RequestHeaders({ @RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"), @RequestHeader(name = "Content-Type", description = "application/json") }) public class ProfiledDocuments { private ProfiledMongoManager manager; private EventManager eventManager = EventManager.getInstance(); // as singleton /** * Instantiates a new profiled documents. * * @param profileID the profile ID * @throws ConfigurationException the configuration exception */ public ProfiledDocuments(@PathParam(InterfaceConstants.Parameters.UCID) String profileID) throws ConfigurationException { log.info("Accessing profile " + profileID); manager = new GuardedMethod() { @Override protected ProfiledMongoManager run() throws Exception { return new ProfiledMongoManager(profileID); } }.execute().getResult(); } /** * Gets the configuration. * * @param profileID the profile ID * @return the configuration */ @GET @Path(InterfaceConstants.Methods.CONFIGURATION_PATH) @Produces(MediaType.APPLICATION_JSON) public Configuration getConfiguration(@PathParam(InterfaceConstants.Parameters.UCID) String profileID) { return new GuardedMethod() { @Override protected Configuration run() throws Exception, WebApplicationException { return ImplementationProvider.get().getProvidedObjectByClass(ConfigurationCache.ConfigurationMap.class) .get(profileID); } }.execute().getResult(); } /** * Creates the new. * * @param d the d * @return the project */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Project createNew(Document d) { Project theNewProject = new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("Creating new Project ({})", manager.getUseCaseDescriptor().getId()); Project toReturn = manager.registerNew(d); log.info("Created new Project ({}, ID {})", manager.getUseCaseDescriptor().getId(), toReturn.getId()); return toReturn; } }.execute().getResult(); Status status = theNewProject.getLifecycleInformation().getLastOperationStatus(); // If latest operation status is not ERROR, notify PROJECT_CREATED event if (status != null && !status.equals(LifecycleInformation.Status.ERROR)) { // notifying the Event.PROJECT_CREATED; ItemObserved item = new ItemObserved(); EventManager.Event event = Event.PROJECT_CREATED; AccountingInfo user = UserUtils.getCurrent().asInfo(); item.setUserCaller(user.getUser()); item.setContext(user.getContext()); item.setOptional(null); item.setEvent(event); item.setProject(theNewProject); item.setUseCaseDescriptor(manager.getUseCaseDescriptor()); log.info("By notifying event ({}, ID {})", event, item.getProjectId()); eventManager.notify(event, item); } return theNewProject; } /** * Update. * * @param documentId the document id * @param d the d * @return the project */ @PUT @Path("{" + InterfaceConstants.Parameters.PROJECT_ID + "}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Project update(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String documentId, Document d) { Project theUpdatedProject = new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("Updating Project ({}, ID {})", manager.getUseCaseDescriptor().getId(), documentId); return manager.update(documentId, d); } }.execute().getResult(); Status status = theUpdatedProject.getLifecycleInformation().getLastOperationStatus(); // If latest operation status is not ERROR, notify PROJECT_UPDATED event if (status != null && !status.equals(LifecycleInformation.Status.ERROR)) { ItemObserved item = new ItemObserved(); EventManager.Event event = Event.PROJECT_UPDATED; AccountingInfo user = UserUtils.getCurrent().asInfo(); item.setUserCaller(user.getUser()); item.setContext(user.getContext()); item.setOptional(null); item.setEvent(event); item.setProject(theUpdatedProject); item.setUseCaseDescriptor(manager.getUseCaseDescriptor()); log.info("By notifying event ({}, ID {})", event, item.getProjectId()); eventManager.notify(event, item); } return theUpdatedProject; } /** * Patch. * * @param documentId the document id * @param d the d * @return the project */ @PATCH @Path("{" + InterfaceConstants.Parameters.PROJECT_ID + "}") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Project patch(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String documentId, Document d) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("Patching Project ({}, ID {})", manager.getUseCaseDescriptor().getId(), documentId); // return manager.patch(documentId,d); throw new WebApplicationException("This method has not yet been implemented!"); } }.execute().getResult(); } /** * Delete. * * @param id the id * @param force the force * @return the boolean */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("{" + InterfaceConstants.Parameters.PROJECT_ID + "}") public Boolean delete(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, @DefaultValue("false") @QueryParam(InterfaceConstants.Parameters.FORCE) Boolean force) { Boolean deleted = new GuardedMethod() { @Override protected Boolean run() throws Exception, WebApplicationException { log.info("Deleting Project ({}, ID {}). Force is {}", manager.getUseCaseDescriptor().getId(), id, force); manager.delete(id, force); return true; } }.execute().getResult(); // If deleted, notify PROJECT_DELETED event if (deleted) { ItemObserved item = new ItemObserved(); EventManager.Event event = Event.PROJECT_DELETED; AccountingInfo user = UserUtils.getCurrent().asInfo(); item.setUserCaller(user.getUser()); item.setContext(user.getContext()); item.setEvent(event); item.setUseCaseDescriptor(manager.getUseCaseDescriptor()); // Referencing delete project Project deletedProject = new Project(); deletedProject.setId(id); deletedProject.setProfileID(manager.getUseCaseDescriptor().getId()); item.setProject(deletedProject); log.info("By notifying event ({}, ID {})", event, item.getProjectId()); eventManager.notify(event, item); } return deleted; } /** * Register file set. * * @param id the id * @param request the request * @return the project */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/" + InterfaceConstants.Methods.REGISTER_FILES_PATH + "/{" + InterfaceConstants.Parameters.PROJECT_ID + "}") public Project registerFileSet(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, RegisterFileSetRequest request) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("UCD {} : Project {} Registering Fileset. Request is {}", manager.getUseCaseDescriptor().getId(), id, request); request.validate(); return manager.registerFileSet(id, request); } }.execute().getResult(); } /** * Delete file set. the Authorization must be a VRE token * * @param id the id * @param force the force * @param path the path must be passed as text in the body * @return the project */ @RequestHeaders({ @RequestHeader(name = "Authorization", description = "VRE Bearer token, see https://dev.d4science.org/how-to-access-resources"), @RequestHeader(name = "Content-Type", description = "application/json") }) @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/" + InterfaceConstants.Methods.DELETE_FILES_PATH + "/{" + InterfaceConstants.Parameters.PROJECT_ID + "}") public Project deleteFileSet(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, @DefaultValue("false") @QueryParam(InterfaceConstants.Parameters.FORCE) Boolean force, @DefaultValue("false") @QueryParam(InterfaceConstants.Parameters.IGNORE_ERRORS) Boolean ignore_errors, String path) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("Deleting FileSet of Project ({}, ID {}) at path {}. Force is {}. Ignore_errors is {}", manager.getUseCaseDescriptor().getId(), id, path, force, ignore_errors); return manager.deleteFileSet(id, path, force, ignore_errors); } }.execute().getResult(); } /** * Perform step. * * @param id the id * @param request the request * @return the project */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/" + InterfaceConstants.Methods.STEP + "/{" + InterfaceConstants.Parameters.PROJECT_ID + "}") public Project performStep(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, PerformStepRequest performStepRequest) { Project theProject = new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("Executing step {} on Project ({},ID,{}) with options {}", performStepRequest.getStepID(), manager.getUseCaseDescriptor().getId(), id, performStepRequest.getOptions()); return manager.performStep(id, performStepRequest.getStepID(), performStepRequest.getOptions()); } }.execute().getResult(); Status status = theProject.getLifecycleInformation().getLastOperationStatus(); // If latest operation status is not ERROR, notify LIFECYCLE_STEP_PERFORMED // event if (status != null && !status.equals(LifecycleInformation.Status.ERROR)) { ItemObserved item = new ItemObserved(); EventManager.Event event = Event.LIFECYCLE_STEP_PERFORMED; AccountingInfo user = UserUtils.getCurrent().asInfo(); item.setUserCaller(user.getUser()); item.setContext(user.getContext()); item.setEvent(event); item.setOptional(OPTIONAL_FIELD.message, performStepRequest.getMessage()); item.setUseCaseDescriptor(manager.getUseCaseDescriptor()); item.setProject(theProject); log.info("By notifying event ({}, ID {})", event, item.getProjectId()); eventManager.notify(event, item); } return theProject; } /** * Force unlock. * * @param id the id * @return the project */ @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/" + InterfaceConstants.Methods.FORCE_UNLOCK + "/{" + InterfaceConstants.Parameters.PROJECT_ID + "}") public Project forceUnlock(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.warn("UCD {}, forcing unlock for Project ID {}", manager.getUseCaseDescriptor().getId(), id); return manager.forceUnlock(id); } }.execute().getResult(); } /** * Sets the access policy. * * @param id the id * @param toSet the to set * @return the project */ @PUT @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/" + InterfaceConstants.Methods.SET_PROJECT_ACCESS_POLICY + "/{" + InterfaceConstants.Parameters.PROJECT_ID + "}") public Project setAccessPolicy(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, Access toSet) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.warn("UCD {}, setting Policy {} Project ID {}", manager.getUseCaseDescriptor().getId(), toSet, id); return manager.setAccessPolicy(id, toSet); } }.execute().getResult(); } // ********************************** READ /** * List. * * @return the iterable */ @GET @Produces(MediaType.APPLICATION_JSON) public Iterable list() { return new GuardedMethod>() { protected Iterable run() throws Exception, WebApplicationException { return manager.query(new QueryRequest()); }; }.execute().getResult(); } /** * Gets the by id. * * @param id the id * @return the by id */ // BY ID @GET @Produces(MediaType.APPLICATION_JSON) @Path("{" + InterfaceConstants.Parameters.PROJECT_ID + "}") public Project getById(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { return manager.getByID(id); } }.execute().getResult(); } /** * Search. * * @param filter the filter * @return the string */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/" + InterfaceConstants.Methods.SEARCH_PATH) public String search(String filter) { return new GuardedMethod() { @Override protected String run() throws Exception, WebApplicationException { QueryRequest req = new QueryRequest(); req.setFilter(Document.parse(filter)); return Serialization.write(manager.query(req)); } }.execute().getResult(); } /** * Query. * * @param queryString the query string * @return the iterable */ @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Path("/" + InterfaceConstants.Methods.QUERY_PATH) public Iterable query(String queryString) { return new GuardedMethod>() { @Override protected Iterable run() throws Exception, WebApplicationException { return manager.query(Serialization.parseQuery(queryString)); } }.execute().getResult(); } // Relationships /** * Gets the relationship chain. * * @param id the id * @param relationshipId the relationship id * @param deep the deep * @return the relationship chain */ @GET @Produces(MediaType.APPLICATION_JSON) @Path("{" + InterfaceConstants.Methods.RELATIONSHIP + "}/{" + InterfaceConstants.Parameters.PROJECT_ID + "}" + "/{" + InterfaceConstants.Parameters.RELATIONSHIP_ID + "}") public String getRelationshipChain(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, @PathParam(InterfaceConstants.Parameters.RELATIONSHIP_ID) String relationshipId, @DefaultValue("false") @QueryParam(InterfaceConstants.Parameters.DEEP) Boolean deep) { return new GuardedMethod() { @Override protected String run() throws Exception, WebApplicationException { return Serialization.write(ProjectAccessImpl .getRelationshipChain(manager.getUseCaseDescriptor().getId(), id, relationshipId, deep)); } }.execute().getResult(); } /** * Sets the relation. * * @param id the id * @param relationshipId the relationship id * @param targetId the target id * @param targetUCD the target UCD * @return the project */ @PUT @Produces(MediaType.APPLICATION_JSON) @Path("{" + InterfaceConstants.Methods.RELATIONSHIP + "}/{" + InterfaceConstants.Parameters.PROJECT_ID + "}" + "/{" + InterfaceConstants.Parameters.RELATIONSHIP_ID + "}") public Project setRelation(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, @PathParam(InterfaceConstants.Parameters.RELATIONSHIP_ID) String relationshipId, @QueryParam(InterfaceConstants.Parameters.TARGET_ID) String targetId, @QueryParam(InterfaceConstants.Parameters.TARGET_UCD) String targetUCD) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("Set relation from Project ({} : {}) [{}]-> ({} : {})", manager.getUseCaseDescriptor().getId(), id, relationshipId, targetUCD, targetId); String toUseTargetUCD = targetUCD; if (toUseTargetUCD == null || toUseTargetUCD.isEmpty()) { log.debug("Target UCD is null, forcing same UCD () as source ", manager.getUseCaseDescriptor().getId()); toUseTargetUCD = manager.getUseCaseDescriptor().getId(); } return manager.setRelation(id, relationshipId, toUseTargetUCD, targetId); } }.execute().getResult(); } /** * Delete relation. * * @param id the id * @param relationshipId the relationship id * @param targetId the target id * @param targetUCD the target UCD * @return the project */ @DELETE @Produces(MediaType.APPLICATION_JSON) @Path("{" + InterfaceConstants.Methods.RELATIONSHIP + "}/{" + InterfaceConstants.Parameters.PROJECT_ID + "}" + "/{" + InterfaceConstants.Parameters.RELATIONSHIP_ID + "}") public Project deleteRelation(@PathParam(InterfaceConstants.Parameters.PROJECT_ID) String id, @PathParam(InterfaceConstants.Parameters.RELATIONSHIP_ID) String relationshipId, @QueryParam(InterfaceConstants.Parameters.TARGET_ID) String targetId, @QueryParam(InterfaceConstants.Parameters.TARGET_UCD) String targetUCD) { return new GuardedMethod() { @Override protected Project run() throws Exception, WebApplicationException { log.info("Deleting relation from Project ({} : {}) [{}]-> ({} : {})", manager.getUseCaseDescriptor().getId(), id, relationshipId, targetUCD, targetId); return manager.deleteRelation(id, relationshipId, targetUCD, targetId); } }.execute().getResult(); } }