Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions cwms-data-api/src/main/java/cwms/cda/ApiServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@
import javax.sql.DataSource;
import org.apache.http.entity.ContentType;
import org.jetbrains.annotations.NotNull;
import org.jooq.exception.DataAccessException;
import org.owasp.html.HtmlPolicyBuilder;
import org.owasp.html.PolicyFactory;

Expand Down Expand Up @@ -380,6 +381,24 @@ public void init() {
CdaError re = new CdaError(e.getMessage());
ctx.status(HttpServletResponse.SC_BAD_REQUEST).json(re);
})
.exception(DataAccessException.class, (e, ctx) -> {
// Whatever Dao is causing this exception to be thrown should be modified.
// The preferred pattern is for the Dao to catch DataAccessExceptions exceptions
// and for the dao to inspect the Oracle error code or error message as necessary
// to transform DataAccessExceptions (and their SQLException causes)
// into specific and appropriate exceptions with
// messages that are helpful and meaningful to end-users.

// CdaError does not include the Oracle exception message b/c this block catches
// all unhandled DataAccessExceptions and we don't know what is in the message
// it is unknown if the message would be safe/appropriate for users to see.
CdaError errResponse = new CdaError("Database Error");
logger.atWarning().withCause(e).log("error on request[%s]: %s",
errResponse.getIncidentIdentifier(), ctx.req.getRequestURI());
ctx.status(500);
ctx.contentType(ContentType.APPLICATION_JSON.toString());
ctx.json(errResponse);
})
.exception(Exception.class, (e, ctx) -> {
CdaError errResponse = new CdaError("System Error");
logger.atWarning().withCause(e).log("error on request[%s]: %s",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cwms.cda.api.errors;

import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.logging.Level;

public class NoDataRateException extends RateException{
// 404 is Not Found - this isn't quite right b/c the rate() or reverse-rate() isn't failing b/c
// the ts isn't found its failing b/c a necessary rating table isn't found.
// 424 is Failed Dependency - this is closer to what we want, but it's not quite right either.

// 422 is Unprocessable Entity. I think this is the closest.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think this is the best/closest option.

I'm going to throw out a wild idea though...... what about 204 No Content?


public static final int HTTP_ERROR_CODE = 422;

public NoDataRateException(String message, SQLException cause) {
super(message, DATABASE_SOURCE, "Error performing rate function: " + message,
HTTP_ERROR_CODE, Level.INFO, new LinkedHashMap<>(), cause);
}

}
13 changes: 10 additions & 3 deletions cwms-data-api/src/main/java/cwms/cda/api/errors/RateException.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,23 @@

package cwms.cda.api.errors;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import javax.servlet.http.HttpServletResponse;

public final class RateException extends ApplicationException {
public class RateException extends ApplicationException {
private static final Level LOG_LEVEL = Level.INFO;

public RateException(String message, String source, String cdaErrorMessage, int cdaHttpErrorCode,
Level logLevel, Map<String, Serializable> details, Throwable cause) {
super(message, source, cdaErrorMessage, cdaHttpErrorCode, logLevel, details, cause);
}

public RateException(String message, SQLException cause) {
super(message, DATABASE_SOURCE, "Error performing rate function: " + message,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, LOG_LEVEL, new HashMap<>(), cause);
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, LOG_LEVEL, new LinkedHashMap<>(), cause);
}
}
32 changes: 25 additions & 7 deletions cwms-data-api/src/main/java/cwms/cda/data/dao/RateDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import static java.util.stream.Collectors.toList;

import cwms.cda.api.errors.NoDataRateException;
import cwms.cda.api.errors.RateException;
import cwms.cda.data.dto.CwmsId;
import cwms.cda.data.dto.TimeSeries;
Expand All @@ -44,6 +45,7 @@
import org.jooq.ConnectionCallable;
import org.jooq.DSLContext;
import org.jooq.exception.DataAccessException;
import org.jspecify.annotations.Nullable;
import usace.cwms.db.jooq.codegen.packages.CWMS_RATING_PACKAGE;
import usace.cwms.db.jooq.codegen.udt.records.DATE_TABLE_TYPE;
import usace.cwms.db.jooq.codegen.udt.records.DOUBLE_TAB_T;
Expand Down Expand Up @@ -72,7 +74,7 @@ public RatedOutput rate(String officeId, String ratingId, RateInputValues input)
STR_TAB_T unitsTab = new STR_TAB_T(input.getInputUnits());
unitsTab.add(input.getOutputUnit());
return CWMS_RATING_PACKAGE.call_RATE(context.configuration(), ratingId,
inputValues, unitsTab, formatBool(input.getRound()), ratingDates, null, "UTC", officeId);
inputValues, unitsTab, formatBool(input.getRound()), ratingDates, null, "UTC", officeId);
});
return new RatedOutputValues(CwmsId.buildCwmsId(officeId, ratingId), outputValues, input.getOutputUnit());
}
Expand Down Expand Up @@ -172,15 +174,31 @@ static RuntimeException handleRateDbError(DataAccessException ex) {
if (cause instanceof SQLException) {
int errorCode = ((SQLException) cause).getErrorCode();
if (errorCode == 20019 || errorCode == 20998) {
String localizedMessage = cause.getLocalizedMessage();
String[] parts = localizedMessage.split("\n");
String message = parts[0];
int index = message.indexOf(":");
if (index >= 0) {
retval = new RateException(message.substring(index + 1), (SQLException) cause);
String message = getMessage(cause);
if( message != null ) {
retval = new RateException(message, (SQLException) cause);
}
} else if (errorCode == 1403){
String message = getMessage(cause);
if( message != null ) {
// firstMessage may be like "no data found"
// or "Table rating has no rating points"
retval = new NoDataRateException(message, (SQLException) cause);
}
}
}
return retval;
}

private static @Nullable String getMessage(Throwable cause) {
String firstMessage = null;
String localizedMessage = cause.getLocalizedMessage();
String[] parts = localizedMessage.split("\n");
String message = parts[0];
int index = message.indexOf(":");
if (index >= 0) {
firstMessage = message.substring(index + 1);
}
return firstMessage;
}
}
Loading