package edu.harvard.iq.dataverse.api; import edu.harvard.iq.dataverse.DataFile; import edu.harvard.iq.dataverse.DataFileServiceBean; import edu.harvard.iq.dataverse.Dataset; import edu.harvard.iq.dataverse.DatasetFieldServiceBean; import edu.harvard.iq.dataverse.DatasetFieldType; import edu.harvard.iq.dataverse.DatasetServiceBean; import edu.harvard.iq.dataverse.DatasetVersion; import edu.harvard.iq.dataverse.DatasetVersionServiceBean; import edu.harvard.iq.dataverse.DatasetVersionServiceBean.RetrieveDatasetVersionResponse; import edu.harvard.iq.dataverse.Dataverse; import edu.harvard.iq.dataverse.DataverseServiceBean; import edu.harvard.iq.dataverse.DvObject; import edu.harvard.iq.dataverse.DvObjectServiceBean; import edu.harvard.iq.dataverse.FileMetadata; import edu.harvard.iq.dataverse.RoleAssignment; import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; import edu.harvard.iq.dataverse.authorization.users.GuestUser; import edu.harvard.iq.dataverse.search.SearchServiceBean; import edu.harvard.iq.dataverse.search.SolrField; import edu.harvard.iq.dataverse.search.SolrQueryResponse; import edu.harvard.iq.dataverse.search.SolrSearchResult; import edu.harvard.iq.dataverse.authorization.users.User; import edu.harvard.iq.dataverse.search.DvObjectSolrDoc; import edu.harvard.iq.dataverse.search.FacetCategory; import edu.harvard.iq.dataverse.search.FileView; import edu.harvard.iq.dataverse.search.IndexAllServiceBean; import edu.harvard.iq.dataverse.search.IndexResponse; import edu.harvard.iq.dataverse.search.IndexServiceBean; import edu.harvard.iq.dataverse.search.IndexUtil; import edu.harvard.iq.dataverse.search.SearchException; import edu.harvard.iq.dataverse.search.SearchFields; import edu.harvard.iq.dataverse.search.SearchFilesServiceBean; import edu.harvard.iq.dataverse.search.SearchUtil; import edu.harvard.iq.dataverse.search.SolrIndexServiceBean; import edu.harvard.iq.dataverse.search.SortBy; import edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder; import static edu.harvard.iq.dataverse.util.json.NullSafeJsonBuilder.jsonObjectBuilder; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.Future; import java.util.logging.Logger; import javax.ejb.EJB; import javax.ejb.EJBException; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.apache.solr.client.solrj.SolrServerException; @Path("admin/index") public class Index extends AbstractApiBean { private static final Logger logger = Logger.getLogger(Index.class.getCanonicalName()); @EJB IndexServiceBean indexService; @EJB IndexAllServiceBean indexAllService; @EJB SolrIndexServiceBean solrIndexService; @EJB DataverseServiceBean dataverseService; @EJB DatasetServiceBean datasetService; @EJB DatasetVersionServiceBean datasetVersionService; @EJB DataFileServiceBean dataFileService; @EJB DvObjectServiceBean dvObjectService; @EJB SolrIndexServiceBean SolrIndexService; @EJB SearchServiceBean searchService; @EJB DatasetFieldServiceBean datasetFieldService; @EJB SearchFilesServiceBean searchFilesService; public static String contentChanged = "contentChanged"; public static String contentIndexed = "contentIndexed"; public static String permsChanged = "permsChanged"; public static String permsIndexed = "permsIndexed"; @GET public Response indexAllOrSubset(@QueryParam("numPartitions") Long numPartitionsSelected, @QueryParam("partitionIdToProcess") Long partitionIdToProcess, @QueryParam("previewOnly") boolean previewOnly) { return indexAllOrSubset(numPartitionsSelected, partitionIdToProcess, false, previewOnly); } @GET @Path("continue") public Response indexAllOrSubsetContinue(@QueryParam("numPartitions") Long numPartitionsSelected, @QueryParam("partitionIdToProcess") Long partitionIdToProcess, @QueryParam("previewOnly") boolean previewOnly) { return indexAllOrSubset(numPartitionsSelected, partitionIdToProcess, true, previewOnly); } private Response indexAllOrSubset(Long numPartitionsSelected, Long partitionIdToProcess, boolean skipIndexed, boolean previewOnly) { try { long numPartitions = 1; if (numPartitionsSelected != null) { if (numPartitionsSelected < 1) { return error(Status.BAD_REQUEST, "numPartitions must be 1 or higher but was " + numPartitionsSelected); } else { numPartitions = numPartitionsSelected; } } List<Long> availablePartitionIds = new ArrayList<>(); for (long i = 0; i < numPartitions; i++) { availablePartitionIds.add(i); } Response invalidParitionIdSelection = error(Status.BAD_REQUEST, "You specified " + numPartitions + " partition(s) and your selected partitionId was " + partitionIdToProcess + " but you must select from these availableParitionIds: " + availablePartitionIds); if (partitionIdToProcess != null) { long selected = partitionIdToProcess; if (!availablePartitionIds.contains(selected)) { return invalidParitionIdSelection; } } else if (numPartitionsSelected == null) { /** * The user has not specified a partitionId and hasn't specified * the number of partitions. Run "index all", the whole thing. */ partitionIdToProcess = 0l; } else { return invalidParitionIdSelection; } JsonObjectBuilder args = Json.createObjectBuilder(); args.add("numPartitions", numPartitions); args.add("partitionIdToProcess", partitionIdToProcess); JsonArrayBuilder availablePartitionIdsBuilder = Json.createArrayBuilder(); for (long i : availablePartitionIds) { availablePartitionIdsBuilder.add(i); } JsonObjectBuilder preview = indexAllService.indexAllOrSubsetPreview(numPartitions, partitionIdToProcess, skipIndexed); if (previewOnly) { preview.add("args", args); preview.add("availablePartitionIds", availablePartitionIdsBuilder); return ok(preview); } JsonObjectBuilder response = Json.createObjectBuilder(); response.add("availablePartitionIds", availablePartitionIdsBuilder); response.add("args", args); /** * @todo How can we expose the String returned from "index all" via * the API? */ Future<JsonObjectBuilder> indexAllFuture = indexAllService.indexAllOrSubset(numPartitions, partitionIdToProcess, skipIndexed, previewOnly); JsonObject workloadPreview = preview.build().getJsonObject("previewOfPartitionWorkload"); int dataverseCount = workloadPreview.getInt("dataverseCount"); int datasetCount = workloadPreview.getInt("datasetCount"); String status = "indexAllOrSubset has begun of " + dataverseCount + " dataverses and " + datasetCount + " datasets."; response.add("message", status); return ok(response); } catch (EJBException ex) { Throwable cause = ex; StringBuilder sb = new StringBuilder(); sb.append(ex + " "); while (cause.getCause() != null) { cause = cause.getCause(); sb.append(cause.getClass().getCanonicalName() + " "); sb.append(cause.getMessage()).append(" "); if (cause instanceof ConstraintViolationException) { ConstraintViolationException constraintViolationException = (ConstraintViolationException) cause; for (ConstraintViolation<?> violation : constraintViolationException.getConstraintViolations()) { sb.append("(invalid value: <<<").append(violation.getInvalidValue()).append(">>> for ").append(violation.getPropertyPath()).append(" at ").append(violation.getLeafBean()).append(" - ").append(violation.getMessage()).append(")"); } } else if (cause instanceof NullPointerException) { for (int i = 0; i < 2; i++) { StackTraceElement stacktrace = cause.getStackTrace()[i]; if (stacktrace != null) { String classCanonicalName = stacktrace.getClass().getCanonicalName(); String methodName = stacktrace.getMethodName(); int lineNumber = stacktrace.getLineNumber(); String error = "at " + stacktrace.getClassName() + "." + stacktrace.getMethodName() + "(" + stacktrace.getFileName() + ":" + lineNumber + ") "; sb.append(error); } } } } if (sb.toString().equals("javax.ejb.EJBException: Transaction aborted javax.transaction.RollbackException java.lang.IllegalStateException ")) { return ok("indexing went as well as can be expected... got java.lang.IllegalStateException but some indexing may have happened anyway"); } else { return error(Status.INTERNAL_SERVER_ERROR, sb.toString()); } } } @GET @Path("clear") public Response clearSolrIndex() { try { JsonObjectBuilder response = SolrIndexService.deleteAllFromSolrAndResetIndexTimes(); return ok(response); } catch (SolrServerException | IOException ex) { return error(Status.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage()); } } @GET @Path("{type}/{id}") public Response indexTypeById(@PathParam("type") String type, @PathParam("id") Long id) { try { if (type.equals("dataverses")) { Dataverse dataverse = dataverseService.find(id); if (dataverse != null) { /** * @todo Can we display the result of indexing to the user? */ Future<String> indexDataverseFuture = indexService.indexDataverse(dataverse); return ok("starting reindex of dataverse " + id); } else { String response = indexService.removeSolrDocFromIndex(IndexServiceBean.solrDocIdentifierDataverse + id); return notFound("Could not find dataverse with id of " + id + ". Result from deletion attempt: " + response); } } else if (type.equals("datasets")) { Dataset dataset = datasetService.find(id); if (dataset != null) { boolean doNormalSolrDocCleanUp = true; Future<String> indexDatasetFuture = indexService.indexDataset(dataset, doNormalSolrDocCleanUp); return ok("starting reindex of dataset " + id); } else { /** * @todo what about published, deaccessioned, etc.? Need * method to target those, not just drafts! */ String response = indexService.removeSolrDocFromIndex(IndexServiceBean.solrDocIdentifierDataset + id + IndexServiceBean.draftSuffix); return notFound("Could not find dataset with id of " + id + ". Result from deletion attempt: " + response); } } else if (type.equals("files")) { DataFile dataFile = dataFileService.find(id); Dataset datasetThatOwnsTheFile = datasetService.find(dataFile.getOwner().getId()); /** * @todo How can we display the result to the user? */ boolean doNormalSolrDocCleanUp = true; Future<String> indexDatasetFuture = indexService.indexDataset(datasetThatOwnsTheFile, doNormalSolrDocCleanUp); return ok("started reindexing " + type + "/" + id); } else { return error(Status.BAD_REQUEST, "illegal type: " + type); } } catch (EJBException ex) { Throwable cause = ex; StringBuilder sb = new StringBuilder(); sb.append("Problem indexing ").append(type).append("/").append(id).append(": "); sb.append(ex).append(" "); while (cause.getCause() != null) { cause = cause.getCause(); sb.append(cause.getClass().getCanonicalName()).append(" "); sb.append(cause.getMessage()).append(" "); if (cause instanceof ConstraintViolationException) { ConstraintViolationException constraintViolationException = (ConstraintViolationException) cause; for (ConstraintViolation<?> violation : constraintViolationException.getConstraintViolations()) { sb.append("(invalid value: <<<").append(violation.getInvalidValue()).append(">>> for ").append(violation.getPropertyPath()).append(" at ").append(violation.getLeafBean()).append(" - ").append(violation.getMessage()).append(")"); } } else if (cause instanceof NullPointerException) { for (int i = 0; i < 2; i++) { StackTraceElement stacktrace = cause.getStackTrace()[i]; if (stacktrace != null) { String classCanonicalName = stacktrace.getClass().getCanonicalName(); String methodName = stacktrace.getMethodName(); int lineNumber = stacktrace.getLineNumber(); String error = "at " + stacktrace.getClassName() + "." + stacktrace.getMethodName() + "(" + stacktrace.getFileName() + ":" + lineNumber + ") "; sb.append(error); } } } } return error(Status.INTERNAL_SERVER_ERROR, sb.toString()); } } @GET @Path("dataset") public Response indexDatasetByPersistentId(@QueryParam("persistentId") String persistentId) { if (persistentId == null) { return error(Status.BAD_REQUEST, "No persistent id given."); } Dataset dataset = null; try { dataset = datasetService.findByGlobalId(persistentId); } catch (Exception ex) { return error(Status.BAD_REQUEST, "Problem looking up dataset with persistent id \"" + persistentId + "\". Error: " + ex.getMessage()); } if (dataset != null) { boolean doNormalSolrDocCleanUp = true; Future<String> indexDatasetFuture = indexService.indexDataset(dataset, doNormalSolrDocCleanUp); JsonObjectBuilder data = Json.createObjectBuilder(); data.add("message", "Reindexed dataset " + persistentId); data.add("id", dataset.getId()); data.add("persistentId", dataset.getGlobalId()); JsonArrayBuilder versions = Json.createArrayBuilder(); for (DatasetVersion version : dataset.getVersions()) { JsonObjectBuilder versionObject = Json.createObjectBuilder(); versionObject.add("semanticVersion", version.getSemanticVersion()); versionObject.add("id", version.getId()); versions.add(versionObject); } data.add("versions", versions); return ok(data); } else { return error(Status.BAD_REQUEST, "Could not find dataset with persistent id " + persistentId); } } /** * This is just a demo of the modular math logic we use for indexAll. */ @GET @Path("mod") public Response indexMod(@QueryParam("partitions") long partitions, @QueryParam("which") long which) { long numObjectToConsider = 100; List<Long> dvObjectsIds = new ArrayList<>(); for (long i = 1; i <= numObjectToConsider; i++) { dvObjectsIds.add(i); } List<Long> mine = IndexUtil.findDvObjectIdsToProcessMod(dvObjectsIds, partitions, which); JsonObjectBuilder response = Json.createObjectBuilder(); response.add("partitions", partitions); response.add("which", which); response.add("mine", mine.toString()); return ok(response); } @GET @Path("perms") public Response indexAllPermissions() { IndexResponse indexResponse = solrIndexService.indexAllPermissions(); return ok(indexResponse.getMessage()); } @GET @Path("perms/{id}") public Response indexPermissions(@PathParam("id") Long id) { DvObject dvObject = dvObjectService.findDvObject(id); if (dvObject == null) { return error(Status.BAD_REQUEST, "Could not find DvObject based on id " + id); } else { IndexResponse indexResponse = solrIndexService.indexPermissionsForOneDvObject(dvObject); return ok(indexResponse.getMessage()); } } @GET @Path("status") public Response indexStatus() { JsonObjectBuilder contentInDatabaseButStaleInOrMissingFromSolr = getContentInDatabaseButStaleInOrMissingFromSolr(); JsonObjectBuilder contentInSolrButNotDatabase; try { contentInSolrButNotDatabase = getContentInSolrButNotDatabase(); } catch (SearchException ex) { return error(Response.Status.INTERNAL_SERVER_ERROR, "Can not determine index status. " + ex.getLocalizedMessage() + ". Is Solr down? Exception: " + ex.getCause().getLocalizedMessage()); } JsonObjectBuilder permissionsInDatabaseButStaleInOrMissingFromSolr = getPermissionsInDatabaseButStaleInOrMissingFromSolr(); JsonObjectBuilder permissionsInSolrButNotDatabase = getPermissionsInSolrButNotDatabase(); JsonObjectBuilder data = Json.createObjectBuilder() .add("contentInDatabaseButStaleInOrMissingFromIndex", contentInDatabaseButStaleInOrMissingFromSolr) .add("contentInIndexButNotDatabase", contentInSolrButNotDatabase) .add("permissionsInDatabaseButStaleInOrMissingFromIndex", permissionsInDatabaseButStaleInOrMissingFromSolr) .add("permissionsInIndexButNotDatabase", permissionsInSolrButNotDatabase); return ok(data); } private JsonObjectBuilder getContentInDatabaseButStaleInOrMissingFromSolr() { List<Dataverse> stateOrMissingDataverses = indexService.findStaleOrMissingDataverses(); List<Dataset> staleOrMissingDatasets = indexService.findStaleOrMissingDatasets(); JsonArrayBuilder jsonStateOrMissingDataverses = Json.createArrayBuilder(); for (Dataverse dataverse : stateOrMissingDataverses) { jsonStateOrMissingDataverses.add(dataverse.getId()); } JsonArrayBuilder datasetsInDatabaseButNotSolr = Json.createArrayBuilder(); for (Dataset dataset : staleOrMissingDatasets) { datasetsInDatabaseButNotSolr.add(dataset.getId()); } JsonObjectBuilder contentInDatabaseButStaleInOrMissingFromSolr = Json.createObjectBuilder() /** * @todo What about files? Currently files are always indexed * along with their parent dataset */ .add("dataverses", jsonStateOrMissingDataverses.build().size()) .add("datasets", datasetsInDatabaseButNotSolr.build().size()); return contentInDatabaseButStaleInOrMissingFromSolr; } private JsonObjectBuilder getContentInSolrButNotDatabase() throws SearchException { List<Long> dataversesInSolrOnly = indexService.findDataversesInSolrOnly(); List<Long> datasetsInSolrOnly = indexService.findDatasetsInSolrOnly(); List<Long> filesInSolrOnly = indexService.findFilesInSolrOnly(); JsonArrayBuilder dataversesInSolrButNotDatabase = Json.createArrayBuilder(); for (Long dataverseId : dataversesInSolrOnly) { dataversesInSolrButNotDatabase.add(dataverseId); } JsonArrayBuilder datasetsInSolrButNotDatabase = Json.createArrayBuilder(); for (Long datasetId : datasetsInSolrOnly) { datasetsInSolrButNotDatabase.add(datasetId); } JsonArrayBuilder filesInSolrButNotDatabase = Json.createArrayBuilder(); for (Long fileId : filesInSolrOnly) { filesInSolrButNotDatabase.add(fileId); } JsonObjectBuilder contentInSolrButNotDatabase = Json.createObjectBuilder() /** * @todo What about files? Currently files are always indexed * along with their parent dataset */ .add("dataverses", dataversesInSolrButNotDatabase.build().size()) .add("datasets", datasetsInSolrButNotDatabase.build().size()) .add("files", filesInSolrButNotDatabase.build().size()); return contentInSolrButNotDatabase; } private JsonObjectBuilder getPermissionsInDatabaseButStaleInOrMissingFromSolr() { List<Long> staleOrMissingPermissions; staleOrMissingPermissions = solrIndexService.findPermissionsInDatabaseButStaleInOrMissingFromSolr(); JsonArrayBuilder stalePermissionList = Json.createArrayBuilder(); for (Long dvObjectId : staleOrMissingPermissions) { stalePermissionList.add(dvObjectId); } return Json.createObjectBuilder() .add("dvobjects", stalePermissionList.build().size()); } private JsonObjectBuilder getPermissionsInSolrButNotDatabase() { List<Long> staleOrMissingPermissions = solrIndexService.findPermissionsInSolrNoLongerInDatabase(); JsonArrayBuilder stalePermissionList = Json.createArrayBuilder(); for (Long dvObjectId : staleOrMissingPermissions) { stalePermissionList.add(dvObjectId); } return Json.createObjectBuilder() .add("dvobjects", stalePermissionList.build().size()); } /** * We use the output of this method to generate our Solr schema.xml * * @todo Someday we do want to have this return a Response rather than a * String per https://github.com/IQSS/dataverse/issues/298 but not yet while * we are trying to ship Dataverse 4.0. */ @GET @Path("solr/schema") public String getSolrSchema() { StringBuilder sb = new StringBuilder(); for (DatasetFieldType datasetField : datasetFieldService.findAllOrderedByName()) { String nameSearchable = datasetField.getSolrField().getNameSearchable(); SolrField.SolrType solrType = datasetField.getSolrField().getSolrType(); String type = solrType.getType(); if (solrType.equals(SolrField.SolrType.EMAIL)) { /** * @todo should we also remove all "email" field types (e.g. * datasetContact) from schema.xml? We are explicitly not * indexing them for * https://github.com/IQSS/dataverse/issues/759 * * "The list of potential collaborators should be searchable" * according to https://github.com/IQSS/dataverse/issues/747 but * it's not clear yet if this means a Solr or database search. * For now we'll keep schema.xml as it is to avoid people having * to update it. If anything, we can remove the email field type * when we do a big schema.xml update for * https://github.com/IQSS/dataverse/issues/754 */ logger.info("email type detected (" + nameSearchable + ") See also https://github.com/IQSS/dataverse/issues/759"); } String multivalued = datasetField.getSolrField().isAllowedToBeMultivalued().toString(); // <field name="datasetId" type="text_general" multiValued="false" stored="true" indexed="true"/> sb.append(" <field name=\"" + nameSearchable + "\" type=\"" + type + "\" multiValued=\"" + multivalued + "\" stored=\"true\" indexed=\"true\"/>\n"); } List<String> listOfStaticFields = new ArrayList<>(); Object searchFieldsObject = new SearchFields(); Field[] staticSearchFields = searchFieldsObject.getClass().getDeclaredFields(); for (Field fieldObject : staticSearchFields) { String name = fieldObject.getName(); String staticSearchField = null; try { staticSearchField = (String) fieldObject.get(searchFieldsObject); } catch (IllegalArgumentException ex) { } catch (IllegalAccessException ex) { } /** * @todo: if you search for "pdf" should you get all pdfs? do we * need a copyField source="filetypemime_s" to the catchall? */ if (listOfStaticFields.contains(staticSearchField)) { return error("static search field defined twice: " + staticSearchField); } listOfStaticFields.add(staticSearchField); } sb.append("---\n"); for (DatasetFieldType datasetField : datasetFieldService.findAllOrderedByName()) { String nameSearchable = datasetField.getSolrField().getNameSearchable(); String nameFacetable = datasetField.getSolrField().getNameFacetable(); if (listOfStaticFields.contains(nameSearchable)) { if (nameSearchable.equals(SearchFields.DATASET_DESCRIPTION)) { // Skip, expected conflct. } else { return error("searchable dataset metadata field conflict detected with static field: " + nameSearchable); } } if (listOfStaticFields.contains(nameFacetable)) { if (nameFacetable.equals(SearchFields.SUBJECT)) { // Skip, expected conflct. } else { return error("facetable dataset metadata field conflict detected with static field: " + nameFacetable); } } // <copyField source="*_i" dest="text" maxChars="3000"/> sb.append(" <copyField source=\"").append(nameSearchable).append("\" dest=\"text\" maxChars=\"3000\"/>\n"); } return sb.toString(); } static String error(String message) { JsonObjectBuilder response = Json.createObjectBuilder(); response.add("status", "ERROR"); response.add("message", message); return "{\n\t\"status\":\"ERROR\"\n\t\"message\":\"" + message.replaceAll("\"", "\\\\\"").replaceAll("\n", "\\\\n") + "\"\n}"; } /** * This method is for integration tests of search. */ @GET @Path("test") public Response searchDebug( @QueryParam("key") String apiToken, @QueryParam("q") String query, @QueryParam("fq") final List<String> filterQueries) { User user = findUserByApiToken(apiToken); if (user == null) { return error(Response.Status.UNAUTHORIZED, "Invalid apikey '" + apiToken + "'"); } Dataverse subtreeScope = dataverseService.findRootDataverse(); String sortField = SearchFields.ID; String sortOrder = SortBy.ASCENDING; int paginationStart = 0; boolean dataRelatedToMe = false; int numResultsPerPage = Integer.MAX_VALUE; SolrQueryResponse solrQueryResponse; try { solrQueryResponse = searchService.search(createDataverseRequest(user), subtreeScope, query, filterQueries, sortField, sortOrder, paginationStart, dataRelatedToMe, numResultsPerPage); } catch (SearchException ex) { return error(Response.Status.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage() + ": " + ex.getCause().getLocalizedMessage()); } JsonArrayBuilder itemsArrayBuilder = Json.createArrayBuilder(); List<SolrSearchResult> solrSearchResults = solrQueryResponse.getSolrSearchResults(); for (SolrSearchResult solrSearchResult : solrSearchResults) { itemsArrayBuilder.add(solrSearchResult.getType() + ":" + solrSearchResult.getNameSort()); } return ok(itemsArrayBuilder); } /** * This method is for integration tests of search. */ @GET @Path("permsDebug") public Response searchPermsDebug( @QueryParam("key") String apiToken, @QueryParam("id") Long dvObjectId) { User user = findUserByApiToken(apiToken); if (user == null) { return error(Response.Status.UNAUTHORIZED, "Invalid apikey '" + apiToken + "'"); } DvObject dvObjectToLookUp = dvObjectService.findDvObject(dvObjectId); if (dvObjectToLookUp == null) { return error(Status.BAD_REQUEST, "Could not find DvObject based on id " + dvObjectId); } List<DvObjectSolrDoc> solrDocs = SolrIndexService.determineSolrDocs(dvObjectToLookUp); JsonObjectBuilder data = Json.createObjectBuilder(); JsonArrayBuilder permissionsData = Json.createArrayBuilder(); for (DvObjectSolrDoc solrDoc : solrDocs) { JsonObjectBuilder dataDoc = Json.createObjectBuilder(); dataDoc.add(SearchFields.ID, solrDoc.getSolrId()); dataDoc.add(SearchFields.NAME_SORT, solrDoc.getNameOrTitle()); JsonArrayBuilder perms = Json.createArrayBuilder(); for (String perm : solrDoc.getPermissions()) { perms.add(perm); } dataDoc.add(SearchFields.DISCOVERABLE_BY, perms); permissionsData.add(dataDoc); } data.add("perms", permissionsData); DvObject dvObject = dvObjectService.findDvObject(dvObjectId); NullSafeJsonBuilder timestamps = jsonObjectBuilder(); timestamps.add(contentChanged, SearchUtil.getTimestampOrNull(dvObject.getModificationTime())); timestamps.add(contentIndexed, SearchUtil.getTimestampOrNull(dvObject.getIndexTime())); timestamps.add(permsChanged, SearchUtil.getTimestampOrNull(dvObject.getPermissionModificationTime())); timestamps.add(permsIndexed, SearchUtil.getTimestampOrNull(dvObject.getPermissionIndexTime())); Set<RoleAssignment> roleAssignments = rolesSvc.rolesAssignments(dvObject); JsonArrayBuilder roleAssignmentsData = Json.createArrayBuilder(); for (RoleAssignment roleAssignment : roleAssignments) { roleAssignmentsData.add(roleAssignment.getRole() + " has been granted to " + roleAssignment.getAssigneeIdentifier() + " on " + roleAssignment.getDefinitionPoint()); } data.add("timestamps", timestamps); data.add("roleAssignments", roleAssignmentsData); return ok(data); } @DELETE @Path("timestamps") public Response deleteAllTimestamps() { int numItemsCleared = dvObjectService.clearAllIndexTimes(); return ok("cleared: " + numItemsCleared); } @DELETE @Path("timestamps/{dvObjectId}") public Response deleteTimestamp(@PathParam("dvObjectId") long dvObjectId) { int numItemsCleared = dvObjectService.clearIndexTimes(dvObjectId); return ok("cleared: " + numItemsCleared); } @GET @Path("filesearch") public Response filesearch(@QueryParam("persistentId") String persistentId, @QueryParam("semanticVersion") String semanticVersion, @QueryParam("q") String userSuppliedQuery) { Dataset dataset = datasetService.findByGlobalId(persistentId); if (dataset == null) { return error(Status.BAD_REQUEST, "Could not find dataset with persistent id " + persistentId); } User user = GuestUser.get(); try { AuthenticatedUser authenticatedUser = findAuthenticatedUserOrDie(); if (authenticatedUser != null) { user = authenticatedUser; } } catch (WrappedResponse ex) { } RetrieveDatasetVersionResponse datasetVersionResponse = datasetVersionService.retrieveDatasetVersionByPersistentId(persistentId, semanticVersion); if (datasetVersionResponse == null) { return error(Status.BAD_REQUEST, "Problem searching for files. Could not find dataset version based on " + persistentId + " and " + semanticVersion); } DatasetVersion datasetVersion = datasetVersionResponse.getDatasetVersion(); FileView fileView = searchFilesService.getFileView(datasetVersion, user, userSuppliedQuery); if (fileView == null) { return error(Status.BAD_REQUEST, "Problem searching for files. Null returned from getFileView."); } JsonArrayBuilder filesFound = Json.createArrayBuilder(); JsonArrayBuilder cards = Json.createArrayBuilder(); JsonArrayBuilder fileIds = Json.createArrayBuilder(); for (SolrSearchResult result : fileView.getSolrSearchResults()) { cards.add(result.getNameSort()); fileIds.add(result.getEntityId()); JsonObjectBuilder fileFound = Json.createObjectBuilder(); fileFound.add("name", result.getNameSort()); fileFound.add("entityId", result.getEntityId().toString()); fileFound.add("datasetVersionId", result.getDatasetVersionId()); fileFound.add("datasetId", result.getParent().get(SearchFields.ID)); filesFound.add(fileFound); } JsonArrayBuilder facets = Json.createArrayBuilder(); for (FacetCategory facetCategory : fileView.getFacetCategoryList()) { facets.add(facetCategory.getFriendlyName()); } JsonArrayBuilder filterQueries = Json.createArrayBuilder(); for (String filterQuery : fileView.getFilterQueries()) { filterQueries.add(filterQuery); } JsonArrayBuilder allDatasetVersionIds = Json.createArrayBuilder(); for (DatasetVersion dsVersion : dataset.getVersions()) { allDatasetVersionIds.add(dsVersion.getId()); } JsonObjectBuilder data = Json.createObjectBuilder(); data.add("filesFound", filesFound); data.add("cards", cards); data.add("fileIds", fileIds); data.add("facets", facets); data.add("user", user.getIdentifier()); data.add("persistentID", persistentId); data.add("query", fileView.getQuery()); data.add("filterQueries", filterQueries); data.add("allDataverVersionIds", allDatasetVersionIds); data.add("semanticVersion", datasetVersion.getSemanticVersion()); return ok(data); } @GET @Path("filemetadata/{dataset_id}") public Response getFileMetadataByDatasetId( @PathParam("dataset_id") long datasetIdToLookUp, @QueryParam("maxResults") int maxResults, @QueryParam("sort") String sortField, @QueryParam("order") String sortOrder ) { JsonArrayBuilder data = Json.createArrayBuilder(); List<FileMetadata> fileMetadatasFound = new ArrayList<>(); try { fileMetadatasFound = dataFileService.findFileMetadataByDatasetVersionId(datasetIdToLookUp, maxResults, sortField, sortOrder); } catch (Exception ex) { return error(Status.BAD_REQUEST, "error: " + ex.getCause().getMessage() + ex); } for (FileMetadata fileMetadata : fileMetadatasFound) { data.add(fileMetadata.getLabel()); } return ok(data); } }