package edu.harvard.iq.dataverse.api;
import edu.harvard.iq.dataverse.Dataverse;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.search.SearchException;
import edu.harvard.iq.dataverse.search.savedsearch.SavedSearch;
import edu.harvard.iq.dataverse.search.savedsearch.SavedSearchFilterQuery;
import edu.harvard.iq.dataverse.search.savedsearch.SavedSearchServiceBean;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.ejb.EJBException;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.DELETE;
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.QueryParam;
import javax.ws.rs.core.Response;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static javax.ws.rs.core.Response.Status.NOT_FOUND;
@Path("admin/savedsearches")
public class SavedSearches extends AbstractApiBean {
private static final Logger logger = Logger.getLogger(SavedSearches.class.getCanonicalName());
@GET
public Response meta() {
JsonArrayBuilder endpoints = Json.createArrayBuilder();
endpoints.add("GET");
endpoints.add("GET /list");
endpoints.add("GET /id");
endpoints.add("POST");
endpoints.add("DELETE /id");
return ok(endpoints);
}
@GET
@Path("list")
public Response list() {
JsonArrayBuilder savedSearchesBuilder = Json.createArrayBuilder();
List<SavedSearch> savedSearches = savedSearchSvc.findAll();
for (SavedSearch savedSearch : savedSearches) {
JsonObjectBuilder thisSavedSearch = toJson(savedSearch);
savedSearchesBuilder.add(thisSavedSearch);
}
JsonObjectBuilder response = Json.createObjectBuilder();
response.add("saved searches", savedSearchesBuilder);
return ok(response);
}
@GET
@Path("{id}")
public Response show(@PathParam("id") long id) {
SavedSearch savedSearch = savedSearchSvc.find(id);
if (savedSearch != null) {
JsonObjectBuilder response = toJson(savedSearch);
return ok(response);
} else {
return error(NOT_FOUND, "Could not find saved search id " + id);
}
}
private JsonObjectBuilder toJson(SavedSearch savedSearch) {
JsonObjectBuilder savedSearchJson = Json.createObjectBuilder();
long savedSearchId = savedSearch.getId();
Dataverse definitionPoint = savedSearch.getDefinitionPoint();
JsonArrayBuilder fqBuilder = Json.createArrayBuilder();
for (SavedSearchFilterQuery fq : savedSearch.getSavedSearchFilterQueries()) {
fqBuilder.add(fq.getFilterQuery());
}
savedSearchJson.add("query", savedSearch.getQuery());
savedSearchJson.add("filterQueries", fqBuilder);
savedSearchJson.add("id", savedSearchId);
savedSearchJson.add("definitionPointId", definitionPoint.getId());
savedSearchJson.add("definitionPointAlias", definitionPoint.getAlias());
savedSearchJson.add("creatorId", savedSearch.getCreator().getId());
return savedSearchJson;
}
@POST
public Response add(JsonObject body) {
if (body == null) {
return error(BAD_REQUEST, "JSON is expected.");
}
String keyForAuthenticatedUserId = "creatorId";
long creatorIdToLookUp;
try {
creatorIdToLookUp = body.getInt(keyForAuthenticatedUserId);
} catch (NullPointerException ex) {
return error(BAD_REQUEST, "Required field missing: " + keyForAuthenticatedUserId);
} catch (ClassCastException ex) {
return error(BAD_REQUEST, "A number is required for " + keyForAuthenticatedUserId);
} catch (Exception ex) {
return error(BAD_REQUEST, "Problem with " + keyForAuthenticatedUserId + ": " + ex);
}
AuthenticatedUser creator = authSvc.findByID(creatorIdToLookUp);
if (creator == null) {
return error(Response.Status.NOT_FOUND, "Could not find user based on " + keyForAuthenticatedUserId + ": " + creatorIdToLookUp);
}
String keyForQuery = "query";
String query;
try {
query = body.getString(keyForQuery);
} catch (NullPointerException ex) {
return error(BAD_REQUEST, "Required field missing: " + keyForQuery);
}
String keyForDefinitionPointId = "definitionPointId";
long dataverseIdToLookup;
try {
dataverseIdToLookup = body.getInt(keyForDefinitionPointId);
} catch (NullPointerException ex) {
return error(BAD_REQUEST, "Required field missing: " + keyForDefinitionPointId);
} catch (ClassCastException ex) {
return error(BAD_REQUEST, "A number is required for " + keyForDefinitionPointId);
} catch (Exception ex) {
return error(BAD_REQUEST, "Problem with " + keyForDefinitionPointId + ": " + ex);
}
Dataverse definitionPoint = dataverseSvc.find(dataverseIdToLookup);
if (definitionPoint == null) {
return error(NOT_FOUND, "Could not find a dataverse based on id " + dataverseIdToLookup);
}
SavedSearch toPersist = new SavedSearch(query, definitionPoint, creator);
String keyForFilterQueries = "filterQueries";
List<SavedSearchFilterQuery> savedSearchFilterQuerys = new ArrayList<>();
try {
JsonArray filterQueries = body.getJsonArray(keyForFilterQueries);
for (int i = 0; i < filterQueries.size(); i++) {
String fq = filterQueries.getString(i);
SavedSearchFilterQuery filterQuery = new SavedSearchFilterQuery(fq, toPersist);
savedSearchFilterQuerys.add(filterQuery);
}
} catch (NullPointerException ex) {
// filter queries are not required, keep going
} catch (Exception ex) {
return error(BAD_REQUEST, "Problem getting filter queries: " + ex);
}
if (!savedSearchFilterQuerys.isEmpty()) {
toPersist.setSavedSearchFilterQueries(savedSearchFilterQuerys);
}
try {
SavedSearch persistedSavedSearch = savedSearchSvc.add(toPersist);
return ok("Added: " + persistedSavedSearch);
} catch (EJBException ex) {
StringBuilder errors = new StringBuilder();
Throwable throwable = ex.getCause();
while (throwable != null) {
errors.append(throwable).append(" ");
throwable = throwable.getCause();
}
return error(BAD_REQUEST, "Problem adding saved search: " + errors);
}
}
@DELETE
@Path("{id}")
public Response delete(@PathParam("id") long doomedId) {
boolean disabled = true;
if (disabled) {
return error(BAD_REQUEST, "Saved Searches can not safely be deleted because links can not safely be deleted. See https://github.com/IQSS/dataverse/issues/1364 for details.");
}
SavedSearch doomed = savedSearchSvc.find(doomedId);
if (doomed == null) {
return error(NOT_FOUND, "Could not find saved search id " + doomedId);
}
boolean wasDeleted = savedSearchSvc.delete(doomedId);
if (wasDeleted) {
return ok(Json.createObjectBuilder().add("Deleted", doomedId));
} else {
return error(INTERNAL_SERVER_ERROR, "Problem deleting id " + doomedId);
}
}
@PUT
@Path("makelinks/all")
public Response makeLinksForAllSavedSearches(@QueryParam("debug") boolean debug) {
JsonObjectBuilder makeLinksResponse;
try {
makeLinksResponse = savedSearchSvc.makeLinksForAllSavedSearches(debug);
return ok(makeLinksResponse);
} catch (CommandException ex) {
return error(BAD_REQUEST, ex.getLocalizedMessage());
} catch (SearchException ex) {
return error(INTERNAL_SERVER_ERROR, ex.getLocalizedMessage());
}
}
@PUT
@Path("makelinks/{id}")
public Response makeLinksForSingleSavedSearch(@PathParam("id") long savedSearchIdToLookUp, @QueryParam("debug") boolean debug) {
SavedSearch savedSearchToMakeLinksFor = savedSearchSvc.find(savedSearchIdToLookUp);
if (savedSearchToMakeLinksFor == null) {
return error(BAD_REQUEST, "Count not find saved search id " + savedSearchIdToLookUp);
}
try {
DataverseRequest dataverseRequest = new DataverseRequest(savedSearchToMakeLinksFor.getCreator(), SavedSearchServiceBean.getHttpServletRequest());
JsonObjectBuilder response = savedSearchSvc.makeLinksForSingleSavedSearch(dataverseRequest, savedSearchToMakeLinksFor, debug);
return ok(response);
} catch (CommandException ex) {
return error(BAD_REQUEST, ex.getLocalizedMessage());
} catch (SearchException ex) {
return error(INTERNAL_SERVER_ERROR, ex.getLocalizedMessage());
}
}
}