Skip to content

Commit

Permalink
PEPPER-1407 Added exception mapper to help handle attempts to insert …
Browse files Browse the repository at this point in the history
…data that will break unique constraints or is otherwise considered duplicative.
  • Loading branch information
zyme committed May 30, 2024
1 parent f92a6f9 commit 8c9d643
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.broadinstitute.dsm.exception.AuthorizationException;
import org.broadinstitute.dsm.exception.DSMBadRequestException;
import org.broadinstitute.dsm.exception.DsmInternalError;
import org.broadinstitute.dsm.exception.DuplicateEntityException;
import org.broadinstitute.dsm.exception.UnsafeDeleteError;
import org.broadinstitute.dsm.jobs.DDPEventJob;
import org.broadinstitute.dsm.jobs.DDPRequestJob;
Expand Down Expand Up @@ -1068,6 +1069,13 @@ private void setupRouteGenericErrorHandlers() {
response.status(401);
response.body(exception.getMessage());
});
exception(DuplicateEntityException.class, (e, request, response) -> {
// Tell the user to retry with a different name. Not logged as an error,
// since this can happen during normal operations.
response.status(400);
response.body(String.format("%s %s is already taken. Please retry with a different value.",
e.getEntityName(), e.getEntityValue()));
});
}

public static void shutdown() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import com.google.gson.Gson;
import lombok.Data;
Expand Down Expand Up @@ -120,42 +121,30 @@ public ViewFilter(String filterName, Map<String, List<Object>> columns, String i
this.realmId = realmId;
}

private static SimpleResult checkUniqueFilterName(String suggestedName) {
SimpleResult results = inTransaction((conn) -> {
SimpleResult dbVals = new SimpleResult();
/**
* Returns true if a filter with the given name exists in the database,
* false if it does not exist.
*/
public static boolean doesFilterExist(String filterName) {
final AtomicBoolean filterExists = new AtomicBoolean(false);
inTransaction((conn) -> {
try (PreparedStatement stmt = conn.prepareStatement(SQL_CHECK_VIEW_NAME)) {
stmt.setString(1, suggestedName);
try {
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
dbVals.resultValue = "Duplicate Name";
}
} catch (Exception e) {
dbVals.resultException = e;
stmt.setString(1, filterName);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
filterExists.set(true);
}
} catch (SQLException ex) {
dbVals.resultException = ex;
} catch (SQLException e) {
throw new DsmInternalError("Error checking uniqueness of filter " + filterName, e);
}
return dbVals;
return null;
});
return results;
return filterExists.get();
}

public static Object saveFilter(@NonNull String filterViewToSave, String userId, @NonNull Map<String, DBElement> columnNameMap,
public static Object saveFilter(@NonNull ViewFilter viewFilter, String userId, @NonNull Map<String, DBElement> columnNameMap,
@NonNull String ddpGroupId) {
ViewFilter viewFilter = new Gson().fromJson(filterViewToSave, ViewFilter.class);
if (viewFilter == null) {
throw new RuntimeException("The request for saving filter doesn't have a ViewFilter");
}
String query;
String suggestedName = viewFilter.getFilterName();
SimpleResult results = checkUniqueFilterName(suggestedName);
if (results.resultValue != null && results.resultValue instanceof String) {
return new Result(500, (String) results.resultValue);
}
if (results.resultException != null) {
throw new RuntimeException(results.resultException);
}
if (StringUtils.isBlank(viewFilter.getQueryItems())) {
Filter[] filters = viewFilter.getFilters();
Map<String, String> queryConditions = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.broadinstitute.dsm.exception;

/**
* Exception thrown when there is a duplicate value
* and the user should try their operation again
* after changing the duplicate value to a unique value
*/
public class DuplicateEntityException extends RuntimeException {

private final String entityName;

private final String entityValue;

/**
* Create a new one in response to an attempt to save
* a duplicte value
* @param entityName the name of the entity, in a way that
* makes sense to the user
* @param entityValue the value of the entity
*/
public DuplicateEntityException(String entityName, String entityValue) {
this.entityName = entityName;
this.entityValue = entityValue;
}

/**
* The kind of entity that has a duplicate value, such as "filter"
*/
public String getEntityName() {
return entityName;
}

/**
* The name of the entity that is duplicated, that the user
* has control over and can change on a retry
*/
public String getEntityValue() {
return entityValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import java.util.Collection;

import com.google.gson.Gson;
import lombok.NonNull;
import org.apache.commons.lang3.StringUtils;
import org.broadinstitute.dsm.db.DDPInstance;
import org.broadinstitute.dsm.db.ViewFilter;
import org.broadinstitute.dsm.exception.DuplicateEntityException;
import org.broadinstitute.dsm.security.RequestHandler;
import org.broadinstitute.dsm.statics.DBConstants;
import org.broadinstitute.dsm.statics.RequestParameter;
Expand Down Expand Up @@ -67,7 +69,14 @@ public Object processRequest(Request request, Response response, String userId)
}
} else if (request.url().contains(RoutePath.SAVE_FILTER)) {
String ddpGroupId = DDPInstance.getDDPGroupId(realm);
return ViewFilter.saveFilter(json, userIdRequest, patchUtil.getColumnNameMap(), ddpGroupId);
ViewFilter viewFilter = new Gson().fromJson(json, ViewFilter.class);

String filterName = viewFilter.getFilterName();
if (ViewFilter.doesFilterExist(filterName)) {
// tell the user to retry with a different name for the filter if the name is already taken
throw new DuplicateEntityException("filter", filterName);
}
return ViewFilter.saveFilter(viewFilter, userIdRequest, patchUtil.getColumnNameMap(), ddpGroupId);
} else if (request.url().contains(RoutePath.FILTER_DEFAULT)) {
if (StringUtils.isBlank(parent)) {
throw new RuntimeException("No parent was sent in the request.");
Expand Down

0 comments on commit 8c9d643

Please sign in to comment.