From f934bd31156b28bfef8210c74b07dc61ba788ed1 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Mon, 8 Jun 2026 14:50:50 -0700 Subject: [PATCH 1/2] Initial update to ratings endpoints to properly report content length --- .../cwms/cda/api/rating/RatingController.java | 30 +- .../cda/api/rating/RatingSpecController.java | 30 +- .../api/rating/RatingTemplateController.java | 26 +- .../api/rating/RatingsControllerTestIT.java | 128 ++++ .../resources/cwms/cda/api/long-rating.xml | 694 ++++++++++++++++++ 5 files changed, 885 insertions(+), 23 deletions(-) create mode 100644 cwms-data-api/src/test/resources/cwms/cda/api/long-rating.xml diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java index 71e4f02b57..e4596827a6 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java @@ -24,7 +24,6 @@ package cwms.cda.api.rating; -import static com.codahale.metrics.MetricRegistry.name; import static cwms.cda.api.Controllers.AT; import static cwms.cda.api.Controllers.BEGIN; import static cwms.cda.api.Controllers.CREATE; @@ -39,8 +38,6 @@ import static cwms.cda.api.Controllers.OFFICE; import static cwms.cda.api.Controllers.RATING_ID; import static cwms.cda.api.Controllers.REPLACE_BASE_CURVE; -import static cwms.cda.api.Controllers.RESULTS; -import static cwms.cda.api.Controllers.SIZE; import static cwms.cda.api.Controllers.STATUS_200; import static cwms.cda.api.Controllers.STATUS_201; import static cwms.cda.api.Controllers.STATUS_404; @@ -55,12 +52,11 @@ import static cwms.cda.api.Controllers.requiredParam; import static cwms.cda.data.dao.JooqDao.getDslContext; -import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; -import cwms.cda.api.BaseCrudHandler; import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import cwms.cda.api.Controllers; +import com.google.common.flogger.FluentLogger; +import cwms.cda.api.BaseCrudHandler; import cwms.cda.api.errors.CdaError; import cwms.cda.api.errors.ExceptionTraceSupport; import cwms.cda.data.dao.JsonRatingUtils; @@ -79,7 +75,6 @@ import cwms.cda.helpers.DateUtils; import hec.data.RatingException; import hec.data.cwmsRating.RatingSet; -import io.javalin.apibuilder.CrudHandler; import io.javalin.core.util.Header; import io.javalin.core.validation.JavalinValidation; import io.javalin.http.Context; @@ -92,10 +87,8 @@ import io.javalin.plugin.openapi.annotations.OpenApiResponse; import java.io.IOException; import java.time.Instant; -import com.google.common.flogger.FluentLogger; import javax.servlet.http.HttpServletResponse; import javax.xml.transform.TransformerException; - import mil.army.usace.hec.cwms.rating.io.xml.RatingXmlFactory; import mil.army.usace.hec.metadata.VerticalDatumException; import org.jetbrains.annotations.NotNull; @@ -353,9 +346,17 @@ public void getAll(@NotNull Context ctx) { end, timezone); ctx.status(HttpServletResponse.SC_OK); - ctx.result(results); addDeprecatedContentTypeWarning(ctx, contentType); updateResultSize(results.length()); + + byte[] bytes = results.getBytes(); + ctx.header(Header.CONTENT_LENGTH, String.valueOf(bytes.length)); + ctx.res.getOutputStream().write(bytes); + } catch (IOException e) { + CdaError re = ExceptionTraceSupport.buildError(ctx, + "Failed to process request to retrieve Ratings", e); + logger.atInfo().withCause(e).log("%s", re); + ctx.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).json(re); } } @@ -426,9 +427,16 @@ public void getOne(@NotNull Context ctx, @NotNull String rating) { String body = getRatingSetString(ctx, method, officeId, rating, beginInstant, endInstant, verticalDatum); if (body != null) { - ctx.result(body); ctx.status(HttpCode.OK); + byte[] bytes = body.getBytes(); + ctx.header(Header.CONTENT_LENGTH, String.valueOf(bytes.length)); + ctx.res.getOutputStream().write(bytes); } + } catch (IOException e) { + CdaError re = ExceptionTraceSupport.buildError(ctx, + "Failed to process request to retrieve RatingSet", e); + logger.atInfo().withCause(e).log("%s", re); + ctx.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).json(re); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java index 41eb577b58..53608198b2 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java @@ -32,6 +32,7 @@ import com.codahale.metrics.Timer; import cwms.cda.api.Controllers; import cwms.cda.api.errors.CdaError; +import cwms.cda.api.errors.ExceptionTraceSupport; import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dao.RatingSpecDao; import cwms.cda.data.dto.rating.RatingSpec; @@ -49,6 +50,7 @@ import io.javalin.plugin.openapi.annotations.OpenApiRequestBody; import io.javalin.plugin.openapi.annotations.OpenApiResponse; +import java.io.IOException; import java.util.Optional; import com.google.common.flogger.FluentLogger; @@ -135,9 +137,17 @@ public void getAll(Context ctx) { ctx.contentType(contentType.toString()); String result = Formats.format(contentType, ratingSpecs); - ctx.result(result); requestResultSize.update(result.length()); ctx.status(HttpServletResponse.SC_OK); + + byte[] bytes = result.getBytes(); + ctx.header(Header.CONTENT_LENGTH, String.valueOf(bytes.length)); + ctx.res.getOutputStream().write(bytes); + } catch (IOException e) { + CdaError re = ExceptionTraceSupport.buildError(ctx, + "Failed to process request to retrieve Ratings", e); + logger.atInfo().log("%s%sfor request %s", re, System.lineSeparator(), ctx.fullUrl()); + ctx.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).json(re); } } @@ -164,7 +174,7 @@ public void getAll(Context ctx) { tags = {TAG} ) @Override - public void getOne(Context ctx, String ratingId) { + public void getOne(Context ctx, @NotNull String ratingId) { String formatHeader = ctx.header(Header.ACCEPT); ContentType contentType = Formats.parseHeader(formatHeader, RatingSpec.class); @@ -179,17 +189,25 @@ public void getOne(Context ctx, String ratingId) { if (template.isPresent()) { String result = Formats.format(contentType, template.get()); - ctx.result(result); ctx.contentType(contentType.toString()); requestResultSize.update(result.length()); ctx.status(HttpServletResponse.SC_OK); + + byte[] bytes = result.getBytes(); + ctx.header(Header.CONTENT_LENGTH, String.valueOf(bytes.length)); + ctx.res.getOutputStream().write(bytes); } else { CdaError re = new CdaError("Unable to find Rating Spec based on parameters " + "given"); logger.atInfo().log("%s%sfor request %s", re, System.lineSeparator(), ctx.fullUrl()); ctx.status(HttpServletResponse.SC_NOT_FOUND).json(re); } + } catch (IOException e) { + CdaError re = ExceptionTraceSupport.buildError(ctx, + "Failed to process request to retrieve Ratings", e); + logger.atInfo().log("%s%sfor request %s", re, System.lineSeparator(), ctx.fullUrl()); + ctx.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).json(re); } } @@ -215,7 +233,7 @@ protected RatingSpecDao getRatingSpecDao(DSLContext dsl) { tags = {TAG} ) @Override - public void create(Context ctx) { + public void create(@NotNull Context ctx) { try (final Timer.Context ignored = markAndTime(CREATE)) { DSLContext dsl = getDslContext(ctx); @@ -247,7 +265,7 @@ public void create(Context ctx) { @OpenApi(ignore = true) @Override - public void update(Context ctx, String locationCode) { + public void update(Context ctx, @NotNull String locationCode) { ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @@ -266,7 +284,7 @@ public void update(Context ctx, String locationCode) { tags = {TAG} ) @Override - public void delete(Context ctx, @NotNull String ratingSpecId) { + public void delete(@NotNull Context ctx, @NotNull String ratingSpecId) { try (final Timer.Context ignored = markAndTime(DELETE)) { DSLContext dsl = getDslContext(ctx); diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java index 6f55c1039b..98dee2dfa0 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java @@ -132,8 +132,15 @@ public void getAll(Context ctx) { ctx.contentType(contentType.toString()); String result = Formats.format(contentType, ratingTemplates); - ctx.result(result); requestResultSize.update(result.length()); + + byte[] bytes = result.getBytes(); + ctx.header(Header.CONTENT_LENGTH, String.valueOf(bytes.length)); + ctx.res.getOutputStream().write(bytes); + } catch (IOException ex) { + CdaError re = new CdaError("Error while writing response "); + logger.atInfo().log("%s%sfor request %s", re, System.lineSeparator(), ctx.fullUrl()); + ctx.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).json(re); } } @@ -164,7 +171,7 @@ private RatingTemplateDao getRatingTemplateDao(DSLContext dsl) { tags = {TAG} ) @Override - public void getOne(Context ctx, String templateId) { + public void getOne(Context ctx, @NotNull String templateId) { String formatHeader = ctx.header(Header.ACCEPT); ContentType contentType = Formats.parseHeader(formatHeader, RatingTemplate.class); @@ -180,17 +187,24 @@ public void getOne(Context ctx, String templateId) { if (template.isPresent()) { String result = Formats.format(contentType, template.get()); - ctx.result(result); ctx.contentType(contentType.toString()); requestResultSize.update(result.length()); ctx.status(HttpServletResponse.SC_OK); + + byte[] bytes = result.getBytes(); + ctx.header(Header.CONTENT_LENGTH, String.valueOf(bytes.length)); + ctx.res.getOutputStream().write(bytes); } else { CdaError re = new CdaError("Unable to find Rating Template based on " + "parameters given"); logger.atInfo().log("%s%sfor request %s", re, System.lineSeparator(), ctx.fullUrl()); ctx.status(HttpServletResponse.SC_NOT_FOUND).json(re); } + } catch (IOException ex) { + CdaError re = new CdaError("Error while writing response "); + logger.atInfo().log("%s%sfor request %s", re, System.lineSeparator(), ctx.fullUrl()); + ctx.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR).json(re); } } @@ -210,7 +224,7 @@ public void getOne(Context ctx, String templateId) { tags = {TAG} ) @Override - public void create(Context ctx) { + public void create(@NotNull Context ctx) { try (final Timer.Context ignored = markAndTime(CREATE)){ DSLContext dsl = getDslContext(ctx); @@ -252,7 +266,7 @@ private static String translateJsonToXml(String body) { @OpenApi(ignore = true) @Override - public void update(Context ctx, String locationCode) { + public void update(@NotNull Context ctx, @NotNull String locationCode) { ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED).json(CdaError.notImplemented()); } @@ -271,7 +285,7 @@ public void update(Context ctx, String locationCode) { tags = {TAG} ) @Override - public void delete(Context ctx, String ratingTemplateId) { + public void delete(@NotNull Context ctx, @NotNull String ratingTemplateId) { try (final Timer.Context ignored = markAndTime(DELETE)){ DSLContext dsl = getDslContext(ctx); 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 7c3f269d5b..e97f4dd7d5 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 @@ -55,13 +55,16 @@ class RatingsControllerTestIT extends DataApiTestIT { static final String EXISTING_LOC = "RatingsControllerTestIT"; + static final String EXISTING_LOC_LONG = "RatingsControlTestLong"; private static final String EXISTING_SPEC = EXISTING_LOC + ".Stage;Flow.COE.Production"; + private static final String EXISTING_SPEC_LONG = EXISTING_LOC_LONG + ".Stage;Flow.COE.Production"; private static final String TEMPLATE = "Stage;Flow.COE"; static final String SPK = "SPK"; @BeforeAll static void beforeAll() throws Exception { store(false); + storeLong(); } @AfterAll @@ -193,6 +196,113 @@ static void store(boolean storeTemplate) throws Exception .statusCode(is(HttpServletResponse.SC_CREATED)); } + static void storeLong() throws Exception + { + //Make sure we always have something. + createLocationWithVerticalDatum(EXISTING_LOC_LONG, true, SPK, VerticalDatum.NAVD88); + + String ratingXml = readResourceFile("cwms/cda/api/long-rating.xml"); + ratingXml = ratingXml.replaceAll("Zanesville", EXISTING_LOC_LONG); + String ratingXml2 = ratingXml.replaceAll("2002-04-09T13:53:01Z", "2016-06-06T00:00:00Z"); + String ratingXml3 = ratingXml.replaceAll("2002-04-09T13:53:01Z", "2085-06-06T00:00:00Z"); + RatingSetContainer container = RatingSetContainerXmlFactory.ratingSetContainerFromXml(ratingXml); + RatingSetContainer container2 = RatingSetContainerXmlFactory.ratingSetContainerFromXml(ratingXml2); + RatingSetContainer container3 = RatingSetContainerXmlFactory.ratingSetContainerFromXml(ratingXml3); + RatingSpecContainer specContainer = container.ratingSpecContainer; + specContainer.officeId = SPK; + specContainer.specOfficeId = SPK; + specContainer.locationId = EXISTING_LOC_LONG; + String specXml = RatingSpecXmlFactory.toXml(specContainer, "", 0, true); + String templateXml = RatingSpecXmlFactory.toXml(specContainer, "", 0); + String setXml = RatingContainerXmlFactory.toXml(container, "", 0, true, false); + String setXml2 = RatingContainerXmlFactory.toXml(container2, "", 0, true, false); + String setXml3 = RatingContainerXmlFactory.toXml(container3, "", 0, true, false); + TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + + //Create Template + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .body(templateXml) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/ratings/template") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)); + + //Create Spec + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .body(specXml) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/ratings/spec") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)); + + //Create the set + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .body(setXml) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .queryParam(STORE_TEMPLATE, false) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/ratings") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)); + + //Create the second set + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .body(setXml2) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .queryParam(STORE_TEMPLATE, false) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/ratings") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)); + + // Create the third set + given() + .log().ifValidationFails(LogDetail.ALL,true) + .contentType(Formats.XMLV2) + .body(setXml3) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, SPK) + .queryParam(STORE_TEMPLATE, false) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/ratings") + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_CREATED)); + } + @ParameterizedTest @EnumSource(GetAllLegacyTest.class) void test_getAll_legacy(GetAllLegacyTest test) { @@ -314,6 +424,24 @@ void test_get_one_latest() { assertEquals("2016-06-06T00:00:00Z", effectiveDate); } + @Test + void get_one_with_content_length() { + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .queryParam(OFFICE, SPK) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/ratings/" + EXISTING_SPEC_LONG) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .header("Content-Length", is("19831")) + .contentType(is(Formats.JSONV2)); + } + enum GetOneTest { DEFAULT(Formats.DEFAULT, Formats.XMLV2), diff --git a/cwms-data-api/src/test/resources/cwms/cda/api/long-rating.xml b/cwms-data-api/src/test/resources/cwms/cda/api/long-rating.xml new file mode 100644 index 0000000000..3e953c2bb1 --- /dev/null +++ b/cwms-data-api/src/test/resources/cwms/cda/api/long-rating.xml @@ -0,0 +1,694 @@ + + + + + + Stage;Flow + COE + + + Stage + LINEAR + ERROR + ERROR + + + Flow + + + + Zanesville.Stage;Flow.COE.Production + Stage;Flow.COE + Zanesville + Production + + LINEAR + NULL + NEAREST + true + false + false + false + + 4444444444 + + 4444444444 + + + + Zanesville.Stage;Flow.COE.Production + ft;cfs + 2002-04-09T13:53:01Z + 2014-06-11T14:46:00Z + true + + + + 2.37744006 + 14.1584233 + + + 2.49935994 + 25.7683304 + + + 2.56031988 + 32.2812051 + + + 2.62128012 + 40.7762591 + + + 2.68224006 + 50.4039869 + + + 2.7432 + 61.4475571 + + + 2.80415994 + 73.3406327 + + + 2.86511988 + 86.3663821 + + + 2.92608012 + 100.241637 + + + 2.98704006 + 114.40006 + + + 3.048 + 129.407989 + + + 3.10895994 + 144.415918 + + + 3.16991988 + 159.990183 + + + 3.23088012 + 176.130786 + + + 3.29184006 + 191.705051 + + + 3.3528 + 208.978328 + + + 3.41375994 + 225.968436 + + + 3.47471988 + 244.940723 + + + 3.53568012 + 264.196179 + + + 3.59664006 + 284.017971 + + + 3.6576 + 305.821943 + + + 3.71855994 + 328.47542 + + + 3.77951988 + 351.128898 + + + 3.84048012 + 373.782375 + + + 3.90144006 + 396.435852 + + + 3.9624 + 416.257645 + + + 4.02335994 + 438.911122 + + + 4.08431988 + 464.396284 + + + 4.14528012 + 487.049761 + + + 4.20624006 + 509.703239 + + + 4.2672 + 532.356716 + + + 4.32815994 + 555.010193 + + + 4.38911988 + 577.66367 + + + 4.45008012 + 597.485463 + + + 4.51104006 + 620.13894 + + + 4.572 + 642.792418 + + + 4.63295994 + 659.782526 + + + 4.69391988 + 682.436003 + + + 4.75488012 + 705.08948 + + + 4.81584006 + 722.079588 + + + 4.8768 + 741.901381 + + + 4.93776024 + 761.723173 + + + 4.99871988 + 784.376651 + + + 5.05968012 + 807.030128 + + + 5.12063976 + 826.85192 + + + 5.1816 + 843.842028 + + + 5.24256024 + 866.495506 + + + 5.30351988 + 886.317298 + + + 5.36448012 + 903.307406 + + + 5.42543976 + 923.129199 + + + 5.4864 + 945.782676 + + + 5.54736024 + 962.772784 + + + 5.60831988 + 979.762892 + + + 5.66928012 + 999.584685 + + + 5.73023976 + 1016.57479 + + + 5.7912 + 1033.5649 + + + 5.82168012 + 1044.89164 + + + 5.85216024 + 1053.38669 + + + 5.88263976 + 1061.88175 + + + 5.91311988 + 1073.20849 + + + 5.9436 + 1081.70354 + + + 6.33983976 + 1254.4363 + + + 6.58368012 + 1349.29774 + + + 6.79703976 + 1438.49581 + + + 7.07136024 + 1557.42656 + + + 7.37616024 + 1676.35732 + + + 7.65048012 + 1798.11976 + + + 7.9248 + 1906.62991 + + + 8.47343976 + 2123.76349 + + + 9.20496024 + 2406.93196 + + + 10.668 + 3114.85313 + + + 12.192 + 4105.94276 + + + 13.716 + 5238.61662 + + + 15.24 + 6456.24102 + + + 17.3736 + 8778.22244 + + + 18.5 + 9750.15 + + + 19.75 + 10850.32 + + + 21.0 + 12100.78 + + + 22.25 + 13520.45 + + + 23.5 + 15125.90 + + + 24.75 + 16935.22 + + + 26.0 + 18965.88 + + + 27.25 + 21235.44 + + + 28.5 + 23765.12 + + + 29.75 + 26575.89 + + + 31.0 + 29689.65 + + + 32.37744006 + 14.1584233 + + + 32.49935994 + 25.7683304 + + + 32.56031988 + 32.2812051 + + + 32.62128012 + 40.7762591 + + + 32.68224006 + 50.4039869 + + + 32.7432 + 61.4475571 + + + 32.80415994 + 73.3406327 + + + 32.86511988 + 86.3663821 + + + 32.92608012 + 100.241637 + + + 32.98704006 + 114.40006 + + + 33.048 + 129.407989 + + + 33.10895994 + 144.415918 + + + 33.16991988 + 159.990183 + + + 33.23088012 + 176.130786 + + + 33.29184006 + 191.705051 + + + 33.3528 + 208.978328 + + + 33.41375994 + 225.968436 + + + 33.47471988 + 244.940723 + + + 33.53568012 + 264.196179 + + + 33.59664006 + 284.017971 + + + 33.6576 + 305.821943 + + + 33.71855994 + 328.47542 + + + 33.77951988 + 351.128898 + + + 33.84048012 + 373.782375 + + + 33.90144006 + 396.435852 + + + 33.9624 + 416.257645 + + + 34.02335994 + 438.911122 + + + 34.08431988 + 464.396284 + + + 34.14528012 + 487.049761 + + + 34.20624006 + 509.703239 + + + 34.2672 + 532.356716 + + + 34.32815994 + 555.010193 + + + 34.38911988 + 577.66367 + + + 34.45008012 + 597.485463 + + + 34.51104006 + 620.13894 + + + 34.572 + 642.792418 + + + 34.63295994 + 659.782526 + + + 34.69391988 + 682.436003 + + + 34.75488012 + 705.08948 + + + 34.81584006 + 722.079588 + + + 34.8768 + 741.901381 + + + 34.93776024 + 761.723173 + + + 34.99871988 + 784.376651 + + + 35.05968012 + 807.030128 + + + 35.12063976 + 826.85192 + + + 35.1816 + 843.842028 + + + 35.24256024 + 866.495506 + + + 35.30351988 + 886.317298 + + + 35.36448012 + 903.307406 + + + 35.42543976 + 923.129199 + + + 35.4864 + 945.782676 + + + 35.54736024 + 962.772784 + + + 35.60831988 + 979.762892 + + + 35.66928012 + 999.584685 + + + 35.73023976 + 1016.57479 + + + 35.7912 + 1033.5649 + + + 35.82168012 + 1044.89164 + + + 35.85216024 + 1053.38669 + + + 35.88263976 + 1061.88175 + + + 35.91311988 + 1073.20849 + + + 35.9436 + 1081.70354 + + + 36.33983976 + 1254.4363 + + + 36.58368012 + 1349.29774 + + + 36.79703976 + 1438.49581 + + + 37.07136024 + 1557.42656 + + + 37.37616024 + 1676.35732 + + + 37.65048012 + 1798.11976 + + + 37.9248 + 1906.62991 + + + 38.47343976 + 2123.76349 + + + 39.20496024 + 2406.93196 + + + + From bd1a54b54f64cd6f3081504b1395922e0f9a513c Mon Sep 17 00:00:00 2001 From: zack-rma Date: Mon, 8 Jun 2026 15:19:07 -0700 Subject: [PATCH 2/2] Add null check to resolve unit test failure --- .../java/cwms/cda/api/rating/RatingSpecController.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java index 53608198b2..2e6923c487 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingSpecController.java @@ -51,6 +51,7 @@ import io.javalin.plugin.openapi.annotations.OpenApiResponse; import java.io.IOException; +import java.io.OutputStream; import java.util.Optional; import com.google.common.flogger.FluentLogger; @@ -196,7 +197,12 @@ public void getOne(Context ctx, @NotNull String ratingId) { byte[] bytes = result.getBytes(); ctx.header(Header.CONTENT_LENGTH, String.valueOf(bytes.length)); - ctx.res.getOutputStream().write(bytes); + OutputStream os = ctx.res.getOutputStream(); + if (os != null) { + os.write(bytes); + } else { + ctx.result(result); + } } else { CdaError re = new CdaError("Unable to find Rating Spec based on parameters " + "given");