diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/JooqDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/JooqDao.java index cdbd10089..31cfd0bf9 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/JooqDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/JooqDao.java @@ -45,7 +45,8 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletResponse; import javax.sql.DataSource; import org.jetbrains.annotations.NotNull; @@ -71,6 +72,8 @@ public abstract class JooqDao extends Dao { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); static ExecuteListener listener = new ExceptionWrappingListener(); + private static Pattern INVALID_OFFICE_ID = Pattern.compile( + "INVALID_OFFICE_ID: \"([^\"]+)\" is not a valid CWMS office id"); public enum DeleteMethod { DELETE_ALL(DeleteRule.DELETE_ALL), @@ -240,6 +243,8 @@ public static RuntimeException wrapException(RuntimeException input) { retVal = buildInvalidUnits(input); } else if (isUnsupportedOperationException(input)) { retVal = buildUnsupportedOperationException(input); + } else if (isInvalidOffice(input)) { + retVal = buildInvalidOffice(input); } return retVal; @@ -302,7 +307,16 @@ public static boolean isNotFound(RuntimeException input) { } } return retVal; - } + } + + public static boolean isInvalidOffice(RuntimeException input) { + return getSqlException(input) + .map(sqlException -> { + return hasCodeOrMessage(sqlException, Collections.singletonList(20010), + Collections.singletonList("INVALID_OFFICE_ID")); + }) + .orElse(false); + } public static boolean isInvalidItem(RuntimeException input) { boolean retVal = false; @@ -509,6 +523,25 @@ private static InvalidItemException buildInvalidUnits(RuntimeException input) { return new InvalidItemException(localizedMessage, cause); } + private static InvalidItemException buildInvalidOffice(RuntimeException input) { + + Throwable cause = (input instanceof DataAccessException) ? input.getCause() : input; + String localizedMessage = cause.getLocalizedMessage(); + if (localizedMessage != null) { + Matcher matcher = INVALID_OFFICE_ID.matcher(localizedMessage); + if (matcher.find()) { + String office = sanitizeOrNull(matcher.group(1)); + if(office != null) { + localizedMessage = "\"" + office + "\" is not a valid CWMS office id"; + } + } + } + if (localizedMessage == null || localizedMessage.isEmpty()) { + localizedMessage = "Invalid Office."; + } + return new InvalidItemException(localizedMessage, cause); + } + public static boolean isUnsupportedOperationException(RuntimeException input) { boolean retVal = false; diff --git a/cwms-data-api/src/test/java/cwms/cda/api/TimeseriesControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/TimeseriesControllerTestIT.java index cfd95daa0..c13a9ab1e 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/TimeseriesControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/TimeseriesControllerTestIT.java @@ -359,6 +359,26 @@ void test_no_office_permissions() throws Exception { .body("message", is("User not authorized for this office.")); } + @Test + void test_invalid_office() { + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV2) + .contentType(Formats.JSONV2) + //Purposefully misspelled office id + .queryParam("office", "NWDW") + .queryParam("name", "Buckhorn.Temp-Water.Inst.1Day.0.cda-test") + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/timeseries/") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_BAD_REQUEST)) + .body("details.message", equalTo("\"NWDW\" is not a valid CWMS office id")); + } + @Test void test_v1_cant_trim() throws Exception { InputStream resource = this.getClass().getResourceAsStream( diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dao/JooqDaoTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dao/JooqDaoTest.java index 349608a38..d4fdf9d5a 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dao/JooqDaoTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dao/JooqDaoTest.java @@ -30,6 +30,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.math.BigDecimal; +import java.sql.SQLException; +import org.jooq.exception.DataAccessException; import org.junit.jupiter.api.Test; final class JooqDaoTest { @@ -60,4 +62,11 @@ void testBuildDouble() { assertEquals(5.5, JooqDao.buildDouble(BigDecimal.valueOf(5.5)), 0.0); assertEquals(0.0, JooqDao.buildDouble(null)); } + + @Test + void testInvalidOfficeId() { + String msg = "ORA-20010: INVALID_OFFICE_ID: \"NWDW\" is not a valid CWMS office id"; + SQLException ex = new SQLException(msg, "", 20010); + assertTrue(JooqDao.isInvalidOffice(new DataAccessException(msg, ex))); + } }