From a878ce742dff02f5a1361d8f4be6fb9433a17516 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Wed, 21 Jan 2026 15:27:37 -0800 Subject: [PATCH 1/2] Fixed bug with time series profile time window. Start and end times were being compared against first and last of data time window instead of data point timestamp, causing 404 if specified time window was not inclusive of the complete profile instance data set. Updated previous/next integration test to have correct usage. --- .../TimeSeriesProfileInstanceDao.java | 21 +++++++++---------- ...TimeSeriesProfileInstanceControllerIT.java | 18 ++++++++-------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/timeseriesprofile/TimeSeriesProfileInstanceDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/timeseriesprofile/TimeSeriesProfileInstanceDao.java index da9789a79..76e04d838 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/timeseriesprofile/TimeSeriesProfileInstanceDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/timeseriesprofile/TimeSeriesProfileInstanceDao.java @@ -10,6 +10,7 @@ import static org.jooq.impl.DSL.using; import static org.jooq.impl.DSL.val; +import com.google.common.flogger.FluentLogger; import cwms.cda.api.errors.NotFoundException; import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dto.CwmsDTOPaginated; @@ -28,7 +29,6 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import com.google.common.flogger.FluentLogger; import org.jetbrains.annotations.NotNull; import org.jooq.Condition; import org.jooq.DSLContext; @@ -240,8 +240,7 @@ public TimeSeriesProfileInstance retrieveTimeSeriesProfileInstance(CwmsId locati final String[] parts = CwmsDTOPaginated.decodeCursor(page); logger.atFine().log("Decoded cursor"); - logger.atFinest().log("%s", lazy(()-> - { + logger.atFinest().log("%s", lazy(() -> { StringBuilder sb = new StringBuilder(); for (String part : parts) { sb.append(part).append("\n"); @@ -325,20 +324,20 @@ public TimeSeriesProfileInstance retrieveTimeSeriesProfileInstance(CwmsId locati // Add the time windows conditions depending on the inclusive flags if (startInclusive && endInclusive) { whereCondition = whereCondition - .and(VIEW_TSV2.FIRST_DATE_TIME.ge(Timestamp.from(startTime))) - .and(VIEW_TSV2.LAST_DATE_TIME.le(Timestamp.from(endTime))); + .and(VIEW_TSV2.DATE_TIME.ge(Timestamp.from(startTime))) + .and(VIEW_TSV2.DATE_TIME.le(Timestamp.from(endTime))); } else if (!startInclusive && endInclusive) { whereCondition = whereCondition - .and(VIEW_TSV2.FIRST_DATE_TIME.greaterThan(Timestamp.from(startTime))) - .and(VIEW_TSV2.LAST_DATE_TIME.le(Timestamp.from(endTime))); + .and(VIEW_TSV2.DATE_TIME.greaterThan(Timestamp.from(startTime))) + .and(VIEW_TSV2.DATE_TIME.le(Timestamp.from(endTime))); } else if (startInclusive) { whereCondition = whereCondition - .and(VIEW_TSV2.FIRST_DATE_TIME.ge(Timestamp.from(startTime))) - .and(VIEW_TSV2.LAST_DATE_TIME.lessThan(Timestamp.from(endTime))); + .and(VIEW_TSV2.DATE_TIME.ge(Timestamp.from(startTime))) + .and(VIEW_TSV2.DATE_TIME.lessThan(Timestamp.from(endTime))); } else { whereCondition = whereCondition - .and(VIEW_TSV2.FIRST_DATE_TIME.greaterThan(Timestamp.from(startTime))) - .and(VIEW_TSV2.LAST_DATE_TIME.lessThan(Timestamp.from(endTime))); + .and(VIEW_TSV2.DATE_TIME.greaterThan(Timestamp.from(startTime))) + .and(VIEW_TSV2.DATE_TIME.lessThan(Timestamp.from(endTime))); } Condition finalWhereCondition = whereCondition; diff --git a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesProfileInstanceControllerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesProfileInstanceControllerIT.java index 681f1945d..344406ac3 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesProfileInstanceControllerIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesProfileInstanceControllerIT.java @@ -1406,11 +1406,10 @@ void test_previous_next_TimeSeriesProfileInstance_Indexed(String format) throws .queryParam(OFFICE, OFFICE_ID) .queryParam(VERSION_DATE, "2024-07-09T12:00:00.00Z") .queryParam(TIMEZONE, "UTC") - .queryParam(START, "2019-09-09T12:45:00.00Z") + .queryParam(START, "2019-09-09T12:49:00.00Z") .queryParam(END, "2019-09-09T14:45:00.00Z") .queryParam(START_TIME_INCLUSIVE, true) .queryParam(PREVIOUS, true) - .queryParam(NEXT, true) .queryParam(END_TIME_INCLUSIVE, true) .queryParam(UNIT, units) .when() @@ -1444,7 +1443,7 @@ void test_previous_next_TimeSeriesProfileInstance_Indexed(String format) throws .queryParam(VERSION_DATE, "2024-07-09T12:00:00.00Z") .queryParam(TIMEZONE, "UTC") .queryParam(START, "2019-09-09T12:45:00.00Z") - .queryParam(END, "2019-09-09T14:45:00.00Z") + .queryParam(END, "2019-09-09T13:15:00.00Z") .queryParam(START_TIME_INCLUSIVE, true) .queryParam(END_TIME_INCLUSIVE, true) .queryParam(NEXT, true) @@ -1465,13 +1464,13 @@ void test_previous_next_TimeSeriesProfileInstance_Indexed(String format) throws .body("time-series-profile.key-parameter", equalTo(tspInstance.getTimeSeriesProfile().getKeyParameter())) .body("time-series-profile.parameter-list.size()", equalTo(2)) - .body("time-series-list.size()", equalTo(26)) + .body("time-series-list.size()", equalTo(25)) .body("time-series-list[\"1568033937000\"].size()", equalTo(2)) .body("time-series-list[\"1568033750000\"].size()", equalTo(2)) .body("time-series-list[\"1568033787000\"].size()", equalTo(2)) ; - // Retrieve instance with both next and previous set to false (should return the first page) + // Retrieve instance with both next and previous set to false and a small time window (should cause data not found error) given() .log().ifValidationFails(LogDetail.ALL, true) .accept(format) @@ -1480,11 +1479,12 @@ void test_previous_next_TimeSeriesProfileInstance_Indexed(String format) throws .queryParam(OFFICE, OFFICE_ID) .queryParam(VERSION_DATE, "2024-07-09T12:00:00.00Z") .queryParam(TIMEZONE, "UTC") - .queryParam(START, "2019-09-09T12:45:00.00Z") - .queryParam(END, "2019-09-09T14:45:00.00Z") + .queryParam(START, "2019-09-09T12:50:00.00Z") + .queryParam(END, "2019-09-09T13:10:00.00Z") .queryParam(START_TIME_INCLUSIVE, true) .queryParam(END_TIME_INCLUSIVE, true) - .queryParam(PREVIOUS, true) + .queryParam(PREVIOUS, false) + .queryParam(NEXT, false) .queryParam(UNIT, units) .when() .redirects().follow(true) @@ -1502,7 +1502,7 @@ void test_previous_next_TimeSeriesProfileInstance_Indexed(String format) throws .body("time-series-profile.key-parameter", equalTo(tspInstance.getTimeSeriesProfile().getKeyParameter())) .body("time-series-profile.parameter-list.size()", equalTo(2)) - .body("time-series-list.size()", equalTo(26)) + .body("time-series-list.size()", equalTo(18)) .body("time-series-list[\"1568033937000\"].size()", equalTo(2)) .body("time-series-list[\"1568033750000\"].size()", equalTo(2)) .body("time-series-list[\"1568033787000\"].size()", equalTo(2)) From be256ee3474247e48eacc83cb952c844942249ac Mon Sep 17 00:00:00 2001 From: zack-rma Date: Wed, 21 Jan 2026 15:49:50 -0800 Subject: [PATCH 2/2] Updated API documentation to better represent previous/next parameter usage. --- .../TimeSeriesProfileInstanceController.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceController.java b/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceController.java index ceca847df..f8cb33795 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/timeseriesprofile/TimeSeriesProfileInstanceController.java @@ -95,10 +95,12 @@ public TimeSeriesProfileInstanceController(MetricRegistry metrics) { + " time series profile instance. Default is true"), @OpenApiParam(name = END_TIME_INCLUSIVE, type = Boolean.class, description = "The end inclusive of the" + " time series profile instance. Default is true"), - @OpenApiParam(name = PREVIOUS, type = boolean.class, description = "Whether to include the previous " - + " time window of the time series profile instance. Default is false"), - @OpenApiParam(name = NEXT, type = boolean.class, description = "Whether to include the next time window " - + "of the time series profile instance. Default is false"), + @OpenApiParam(name = PREVIOUS, type = boolean.class, description = "Whether to include the data point " + + "with the closest timestamp prior to the specified start of the time window for the time series " + + "profile instance. Default is false"), + @OpenApiParam(name = NEXT, type = boolean.class, description = "Whether to include the data point with " + + "the closest timestamp after the specified end of the time window for the time series profile " + + "instance. Default is false"), @OpenApiParam(name = MAX_VERSION, type = boolean.class, description = "Whether to use the max version" + " date of the time series profile instance. Default is false. If no version date is provided, and" + " maxVersion is false, the min version date will be used."),