package org.gcube.application.geoportalcommon.geoportal; import static org.gcube.application.geoportal.client.plugins.GeoportalAbstractPlugin.projects; import java.io.File; import java.io.FileNotFoundException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import org.bson.Document; import org.gcube.application.geoportal.common.faults.InvalidRequestException; import org.gcube.application.geoportal.common.model.configuration.Archive; 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.relationships.RelationshipNavigationObject; import org.gcube.application.geoportal.common.model.rest.CreateRelationshipRequest; import org.gcube.application.geoportal.common.model.rest.DeleteRelationshipRequest; import org.gcube.application.geoportal.common.model.rest.PerformStepRequest; import org.gcube.application.geoportal.common.model.rest.QueryRequest; import org.gcube.application.geoportal.common.model.rest.QueryRequest.OrderedRequest; import org.gcube.application.geoportal.common.model.rest.QueryRequest.OrderedRequest.Direction; import org.gcube.application.geoportal.common.model.rest.QueryRequest.PagedRequest; import org.gcube.application.geoportal.common.model.rest.RegisterFileSetRequest; import org.gcube.application.geoportal.common.rest.Projects; import org.gcube.application.geoportal.common.utils.FileSets; import org.gcube.application.geoportal.common.utils.StorageUtils; import org.gcube.application.geoportalcommon.shared.SearchingFilter; import org.gcube.application.geoportalcommon.shared.SearchingFilter.LOGICAL_OP; import org.gcube.application.geoportalcommon.shared.SearchingFilter.ORDER; import org.gcube.application.geoportalcommon.shared.WhereClause; import org.gcube.application.geoportalcommon.shared.geoportal.config.ItemFieldDV; import org.gcube.application.geoportalcommon.shared.geoportal.project.PhaseDV; import org.gcube.com.fasterxml.jackson.databind.ObjectMapper; import org.json.JSONArray; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.spi.json.JsonOrgJsonProvider; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; /** * The Class ProjectsCaller. * * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it * * Mar 17, 2022 */ public class ProjectsCaller { public static final String DOCUMENT_STORE_COLLECTION = "DOCUMENT-STORE-COLLECTION"; private static Logger LOG = LoggerFactory.getLogger(GeoportalClientCaller.class); /** * Gets the client. * * @param profileID the profile ID * @return the client */ public Projects getClient(String profileID) { LOG.info("getClient called for profileID={}", profileID); return projects(profileID).build(); } /** * Creates the new. * * @param profileID the profile ID * @param jsonDocument the json document * @return the project * @throws RemoteException the remote exception */ public Project createNew(String profileID, String jsonDocument) throws RemoteException { LOG.info("createNew called on profileID={}", profileID); Document myDocument = Document.parse(jsonDocument); Projects client = getClient(profileID); Project project = client.createNew(myDocument); return project; } /** * Register file set. * * @param profileID the profile ID * @param project the project * @param theFile the the file * @param parentPath the parent path * @param fieldName the field name * @param fieldDefinition the field definition * @return the project * @throws RemoteException the remote exception * @throws FileNotFoundException the file not found exception * @throws JsonProcessingException the json processing exception * @throws InvalidRequestException the invalid request exception */ public Project registerFileSet(String profileID, Project project, File theFile, String parentPath, String fieldName, String fieldDefinition) throws RemoteException, FileNotFoundException, JsonProcessingException, InvalidRequestException { LOG.info( "registerFileSet called with [profileID={}, projectID={}, theFile={}, parentPath={}, fieldName={}, fieldDefinition={}", parentPath, project.getId(), theFile, parentPath, fieldName, fieldDefinition); Projects client = getClient(profileID); // Prepare request RegisterFileSetRequest fsRequest = FileSets.prepareRequest(new StorageUtils(), parentPath, fieldName, fieldDefinition, theFile); project = client.registerFileSet(project.getId(), fsRequest); LOG.trace("Resulting Project : " + project); return project; } /** * Gets the list for profile ID. * * @param profileID the profile ID * @return the list for profile ID * @throws Exception the exception */ public List getListForProfileID(String profileID) throws Exception { LOG.info("getListForProfileID called for profileID: {}", profileID); Projects client = (Projects) getClient(profileID); List listProjects = new ArrayList(); Iterator projects = client.query(new QueryRequest()); for (Iterator iterator = projects; projects.hasNext();) { Project prg = (Project) iterator.next(); listProjects.add(prg); } LOG.info("returning {} project/s", listProjects.size(), Project.class.getName()); return listProjects; } /** * Gets the project by ID. * * @param profileID the profile ID * @param projectID the project ID * @return the project by ID * @throws Exception the exception */ public Project getProjectByID(String profileID, String projectID) throws Exception { LOG.info("getProjectByID called for profileID: {}, projectID: {}", profileID, projectID); Projects client = (Projects) getClient(profileID); Project project = client.getById(projectID); LOG.info("returning project {}", project != null ? project.getId() : null); return project; } /** * Gets the relationship chain. * * @param profileID the profile ID * @param projectID the project ID * @param relationID the relation ID * @param deep the deep * @return the relationship chain * @throws Exception the exception */ public Iterator getRelationshipChain(String profileID, String projectID, String relationID, Boolean deep) throws Exception { LOG.info("getRelationshipChain called for projectID: {}, relationID: {}", projectID, projectID); Projects client = (Projects) getClient(profileID); return client.getRelationshipChain(projectID, relationID, deep); } /** * Gets the configuration. * * @param profileID the profile ID * @return the configuration * @throws Exception the exception */ public Configuration getConfiguration(String profileID) throws Exception { LOG.info("getConfiguration called for profileID: {} ", profileID); Projects client = (Projects) getClient(profileID); Configuration config = client.getConfiguration(); LOG.debug("returning: {} ", config); return config; } /** * Gets the total document. * * @param profileID the profile ID * @return the total document * @throws Exception the exception */ public Integer getTotalDocument(String profileID) throws Exception { LOG.info("getTotalDocument called for profileID: {}", profileID); Projects client = (Projects) getClient(profileID); Configuration config = client.getConfiguration(); List listArchives = config.getArchives(); for (Archive archive : listArchives) { String theType = archive.getString("_type"); if (theType.equalsIgnoreCase(DOCUMENT_STORE_COLLECTION)) { String totalDocumentAre = archive.get("count").toString(); int total = Integer.parseInt(totalDocumentAre); LOG.info("total docs for profileID: {}, are: {}", profileID, total); return total; } } return null; } /** * Gets the total document. * * @param profileID the profile ID * @return the total document * @throws Exception the exception */ public List getIDsPhases(String profileID) throws Exception { LOG.info("getIDsPhases called for profileID: {}", profileID); Projects client = (Projects) getClient(profileID); Configuration config = client.getConfiguration(); List listArchives = config.getArchives(); for (Archive archive : listArchives) { String theType = archive.getString("_type"); if (theType.equalsIgnoreCase(DOCUMENT_STORE_COLLECTION)) { com.jayway.jsonpath.Configuration configuration = com.jayway.jsonpath.Configuration.builder() .jsonProvider(new JsonOrgJsonProvider()).build(); String toJSON = archive.toJson(); JSONObject jObject = new JSONObject(toJSON); JsonPath jsonPath = JsonPath.compile("$.countByPhase[*]._id.phase"); JSONArray phases = jsonPath.read(jObject, configuration); HashSet listdata = new HashSet(phases.length()); for (int i = 0; i < phases.length(); i++) { listdata.add(phases.get(i).toString()); } List idsPhases = listdata.stream().collect(Collectors.toList()); LOG.info("getIDsPhases returning: {}", idsPhases); return idsPhases; } } LOG.info("getIDsPhases returning null"); return null; } public Integer getCountByPhaseFor(String profileID, String phase, String status) throws Exception { LOG.info("getCountByPhaseFor called for profileID: {}, phase: {}, status: {}", profileID, phase, status); Projects client = (Projects) getClient(profileID); Configuration config = client.getConfiguration(); List listArchives = config.getArchives(); Integer count = null; for (Archive archive : listArchives) { String theType = archive.getString("_type"); if (theType.equalsIgnoreCase(DOCUMENT_STORE_COLLECTION)) { com.jayway.jsonpath.Configuration configuration = com.jayway.jsonpath.Configuration.builder() .jsonProvider(new JsonOrgJsonProvider()).build(); String toJSON = archive.toJson(); JSONObject jObject = new JSONObject(toJSON); String query = String.format("$.countByPhase[*][?(@._id.phase == '%s' && @._id.status == '%s')].count", phase, status); LOG.debug("Performing query: " + query); JsonPath jsonPath = JsonPath.compile(query); JSONArray counts = jsonPath.read(jObject, configuration); try { count = counts.getInt(0); } catch (Exception e) { LOG.warn("getCountByPhaseFor error: " + e.getLocalizedMessage()); } } } LOG.info("getCountByPhaseFor returning: " + count); return count; } /** * Gets the phases into document store collection. * * @param profileID the profile ID * @return the phases into document store collection * @throws Exception the exception */ public PhaseDV[] getPhasesIntoDocumentStoreCollection(String profileID) throws Exception { LOG.info("getPhases called for profileID: {}", profileID); Projects client = (Projects) getClient(profileID); Configuration config = client.getConfiguration(); List listArchives = config.getArchives(); for (Archive archive : listArchives) { String theType = archive.getString("_type"); if (theType.equalsIgnoreCase(DOCUMENT_STORE_COLLECTION)) { com.jayway.jsonpath.Configuration configuration = com.jayway.jsonpath.Configuration.builder() .jsonProvider(new JsonOrgJsonProvider()).build(); String toJSON = archive.toJson(); JSONObject jObject = new JSONObject(toJSON); JsonPath jsonPath = JsonPath.compile("$.countByPhase[*]"); JSONArray phases = jsonPath.read(jObject, configuration); final ObjectMapper objectMapper = new ObjectMapper(); PhaseDV[] phasesArr = objectMapper.readValue(phases.toString(), PhaseDV[].class); LOG.info("getPhases returning {} PhaseDVs", phasesArr.length); return phasesArr; } } LOG.info("getPhases returning null"); return null; } /** * Perform step. * * @param profileID the profile ID * @param projectID the project ID * @param stepID the step ID * @param optionalMessage the optional message * @param options the options * @return the project * @throws Exception the exception */ public Project performStep(String profileID, String projectID, String stepID, String optionalMessage, Document options) throws Exception { LOG.info("performStep called for profileID: {}, projectID: {}. Optional message exists?: {}", profileID, projectID, optionalMessage!=null); Projects client = (Projects) getClient(profileID); PerformStepRequest request = new PerformStepRequest(stepID, optionalMessage, options); Project project = client.performStep(projectID, request); LOG.info("performStep returning project ID: " + project.getId()); return project; } /** * Delete project. * * @param profileID the profile ID * @param projectID the project ID * @param force the force * @throws RemoteException the remote exception */ public void deleteProject(String profileID, String projectID, boolean force) throws RemoteException { LOG.info("deleteProject called for projectID {}", projectID); Projects client = (Projects) getClient(profileID); if (force) client.deleteById(projectID, force); else client.deleteById(projectID); return; } /** * Update project. * * @param profileID the profile ID * @param projectID the project ID * @param updatedDocument the updated document * @return the project * @throws RemoteException the remote exception */ public Project updateProject(String profileID, String projectID, Document updatedDocument) throws RemoteException { LOG.info("updateProject called for projectID {}", projectID); Projects client = (Projects) getClient(profileID); return client.updateDocument(projectID, updatedDocument); } /** * Creates the relationship. * * @param fromProfileID the from profile ID * @param fromProjectID the from project ID * @param relationshipId the relationship id * @param toProfileID the to profile ID * @param toProjectID the to project ID * @throws RemoteException the remote exception */ public void createRelationship(String fromProfileID, String fromProjectID, String relationshipId, String toProfileID, String toProjectID) throws RemoteException { LOG.info("createRelationship called for fromProfileID {} and fromProjectID {}", fromProfileID, fromProjectID); LOG.info("create relationshipName {}", relationshipId); LOG.info("to toProfileID {} and toProjectID {}", toProfileID, toProjectID); Projects client = (Projects) getClient(fromProfileID); CreateRelationshipRequest request = new CreateRelationshipRequest(fromProjectID, relationshipId, toProjectID, toProfileID); client.setRelation(request); return; } /** * Delete relationship. * * @param fromProfileID the from profile ID * @param fromProjectID the from project ID * @param relationshipId the relationship id * @param toProfileID the to profile ID * @param toProjectID the to project ID * @throws RemoteException the remote exception */ public void deleteRelationship(String fromProfileID, String fromProjectID, String relationshipId, String toProfileID, String toProjectID) throws RemoteException { LOG.info("deleteRelationship called for fromProfileID {} and fromProjectID {}", fromProfileID, fromProjectID); LOG.info("deleteRelationship relationshipName {}", relationshipId); LOG.info("to toProfileID {} and toProjectID {}", toProfileID, toProjectID); Projects client = (Projects) getClient(fromProfileID); DeleteRelationshipRequest request = new DeleteRelationshipRequest(fromProjectID, relationshipId, toProjectID, toProfileID); client.deleteRelation(request); } /** * Delete fileset. * * @param profileID the profile ID * @param projectID the project ID * @param jsonPathToFileset the json path to fileset * @param force the force * @param ignoreErrors the ignore errors * @return the project * @throws RemoteException the remote exception */ public Project deleteFileset(String profileID, String projectID, String jsonPathToFileset, Boolean force, Boolean ignoreErrors) throws RemoteException { LOG.info("deleteFileset called for profileID {} and projectID {}, fileset path: {}", profileID, projectID, jsonPathToFileset); Projects client = (Projects) getClient(profileID); ignoreErrors = ignoreErrors == null ? false : ignoreErrors; Project project = client.deleteFileSet(projectID, jsonPathToFileset, force, ignoreErrors); LOG.info("fileset {} deleted", jsonPathToFileset); LOG.debug("returning new project: {} ", project.getTheDocument()); return project; } /** * Simple query. * * @param profileID the profile ID * @param filter the filter * @return the iterator * @throws Exception the exception */ public Iterator simpleQuery(String profileID, Document filter) throws Exception { LOG.info("simpleQuery called for profileID {} and filter {}", profileID, filter); try { Projects geoportalClient = getClient(profileID); QueryRequest request = new QueryRequest(); request.setFilter(filter); return geoportalClient.query(request); } catch (Exception e) { LOG.error("Error on performing query: " + filter, e); throw new Exception("Error occurred on performing query " + filter + ". Error: " + e.getMessage()); } } /** * Query on mongo. * * @param profileID the profile ID * @param totalItems the total items * @param offset the offset * @param limit the limit * @param filter the filter * @return the iterator * @throws Exception the exception */ public Iterator queryOnMongo(String profileID, Integer totalItems, Integer offset, Integer limit, SearchingFilter filter) throws Exception { LOG.debug("queryOnMongo called"); try { Projects geoportalClient = getClient(profileID); if (totalItems == null || totalItems < 0) { // TODO MUST BE REPLACED BY COUNT List listOfProjects = getListForProfileID(profileID); int listConcessioniSize = listOfProjects.size(); totalItems = listConcessioniSize; } Integer offsetIndex = offset; Integer limitIndex = limit; if (offset == null || offset < 0) { offsetIndex = 0; } if (limit == null || limit < 0) { limitIndex = totalItems; } Direction sDirection = null; List orderingFields = new ArrayList(); if (filter == null) { LOG.info("No filter found, creating empty filter"); filter = new SearchingFilter(); } ORDER order = filter.getOrder(); if (order == null) { order = ORDER.ASC; LOG.info("No direction/order found, using default: " + order); } switch (order) { case ASC: sDirection = Direction.ASCENDING; break; case DESC: sDirection = Direction.DESCENDING; break; } List orderByFields = filter.getOrderByFields(); if (orderByFields == null) { orderByFields = new ArrayList(); } // if (orderByFields.isEmpty()) { // ItemFieldDV orderD = new ItemFieldDV("", Arrays.asList("name"), null, false, false, false); // LOG.info("Order by is null, adding default: " + orderD); // orderByFields.add(orderD); // } for (ItemFieldDV itemField : orderByFields) { if (itemField.getJsonFields() != null) { for (String jsonFieldPath : itemField.getJsonFields()) { // String fieldFullPath = String.format("%s.%s", itemField.getProjection(), // field); orderingFields.add(jsonFieldPath); } } } Map projection = filter.getProjection(); Document projectionDocument = null; if (projection != null && !projection.isEmpty()) { projectionDocument = new Document(projection); } QueryRequest request = new QueryRequest(); PagedRequest paging = new PagedRequest(); paging.setOffset(offsetIndex); paging.setLimit(limitIndex); request.setPaging(paging); OrderedRequest ordering = new OrderedRequest(); ordering.setDirection(sDirection); ordering.setFields(orderingFields); request.setOrdering(ordering); Document query = new Document(); if (filter.getConditions() != null) { for (WhereClause whereClause : filter.getConditions()) { LOGICAL_OP searchWithOperator = whereClause.getOperator(); if (searchWithOperator == null) { searchWithOperator = LOGICAL_OP.OR; } if (whereClause.getSearchInto() != null && !whereClause.getSearchInto().isEmpty()) { Map searchFields = whereClause.getSearchInto(); BasicDBObjectBuilder builder = BasicDBObjectBuilder.start(); for (String key : searchFields.keySet()) { // using regex and case-insensitive BasicDBObject bs = new BasicDBObject(); bs.append("$regex", searchFields.get(key)); bs.append("$options", "i"); builder.append(key, bs); } // Building list of Document in OR clause BasicDBList list = new BasicDBList(); Map map = builder.get().toMap(); for (Object key : map.keySet()) { BasicDBObject value = (BasicDBObject) map.get(key); Document doc = new Document((String) key, value); list.add(doc); } // query = new Document(); query.put(searchWithOperator.getOperator(), list); // BasicDBObject bs = new BasicDBObject(); // bs.append("$eq", "PASSED"); // query.put("report.status", bs); } } } if (projectionDocument != null) { request.setProjection(projectionDocument); // THE first field specified in the projection must be not null BasicDBObject bsNotEqualEmpty = new BasicDBObject(); bsNotEqualEmpty.append("$ne", null); Optional firstKey = projection.keySet().stream().findFirst(); if (firstKey.isPresent()) { String firstFieldPath = firstKey.get(); query.append(firstFieldPath, bsNotEqualEmpty); } } // REMOVING DIRTY DOCUMENTS /* * BasicDBObject bsValid_Document = new BasicDBObject(); * bsValid_Document.append("$exists", true); bsValid_Document.append("$ne", * null); query.append("_theDocument", bsValid_Document); * * BasicDBObject bsValidLfc = new BasicDBObject(); bsValidLfc.append("$ne", * null); query.append("_lifecycleInformation._phase", bsValidLfc); */ // BasicDBObject bsDocumentExists = new BasicDBObject(); // bsDocumentExists.append("$exists", false); // query.append("theDocument", bsDocumentExists); // BasicDBObject bsNotEqualEmpty = new BasicDBObject(); // bsNotEqualEmpty.append("$ne", null); // query.append("_theDocument.nome", bsNotEqualEmpty); request.setFilter(query); LOG.info("Paging offset: " + offsetIndex + ", limit: " + limitIndex); LOG.info("Direction: " + sDirection); LOG.info("Projection: " + projectionDocument); LOG.info("Order by Fields: " + ordering); LOG.info("Search for conditions: " + filter.getConditions()); if (query != null) { LOG.info("Search query to JSON: " + query.toJson()); } return geoportalClient.query(request); } catch (Exception e) { LOG.error("Error on loading paginated and filtered list of Project: ", e); throw new Exception("Error occurred on loading list of Project. Error: " + e.getMessage()); } } }