From b8e4bea906640668c6773d261f5dd0dd24687606 Mon Sep 17 00:00:00 2001 From: Adam Korynta Date: Thu, 22 Oct 2026 10:19:06 -0700 Subject: [PATCH] Handle missing rating specs on /ratings/{rating-id} endpoint with 404 --- .../java/cwms/cda/data/dao/RatingSetDao.java | 23 +++++++++++++++++-- .../api/rating/RatingsControllerTestIT.java | 17 ++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java index 48309a622..2bcb26d28 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/RatingSetDao.java @@ -27,12 +27,14 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import cwms.cda.api.errors.NotFoundException; import cwms.cda.data.dto.VerticalDatumInfo; import cwms.cda.data.dto.rating.RatingSpec; import hec.data.RatingException; import hec.data.cwmsRating.RatingSet; import java.io.IOException; import java.sql.Connection; +import java.sql.SQLException; import java.sql.Timestamp; import java.time.Instant; import java.util.List; @@ -125,10 +127,13 @@ public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, } catch (DataAccessException ex) { + if (JooqDao.isNotFound(ex)) { + throw new NotFoundException("Rating specification not found: " + specificationId, ex); + } Throwable cause = ex.getCause(); if (cause instanceof RatingException) { - if (cause.getMessage().contains("contains no rating templates")) { - return null; + if (isMissingSpec(cause)) { + throw new NotFoundException("Rating specification not found: " + specificationId, cause); } throw (RatingException) cause; @@ -138,6 +143,20 @@ public RatingSet retrieve(RatingSet.DatabaseLoadMethod method, String officeId, return retval[0]; } + private static boolean isMissingSpec(Throwable throwable) { + for (Throwable t = throwable; t != null; t = t.getCause()) { + if (t instanceof SQLException && JooqDao.isNotFound(new DataAccessException("rating", t))) { + return true; + } + String message = t.getMessage(); + if (message != null && (message.contains("contains no rating templates") + || message.contains("contains no rating specifications"))) { + return true; + } + } + return false; + } + // store/update @Override public void store(String ratingSetXml, boolean replaceBaseCurve, VerticalDatum vd) throws IOException, RatingException { diff --git a/cwms-data-api/src/test/java/cwms/cda/api/rating/RatingsControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/rating/RatingsControllerTestIT.java index d0c017e1e..d378b6a8a 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/rating/RatingsControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/rating/RatingsControllerTestIT.java @@ -444,6 +444,23 @@ void get_one_with_content_length() { .contentType(is(Formats.JSONV2)); } + @ParameterizedTest + @EnumSource(GetOneTest.class) + void test_get_one_spec_not_found_returns_404(GetOneTest test) { + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(test.accept) + .queryParam(OFFICE, SPK) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/ratings/NoSuchLoc.Stage;Flow.Standard.NoSuchSpec") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_NOT_FOUND)); + } + enum GetOneTest { DEFAULT(Formats.DEFAULT, Formats.XMLV2),