From bec831dab25014a05ac84627a1c26e57821f8457 Mon Sep 17 00:00:00 2001 From: zack-rma Date: Thu, 23 Apr 2026 14:53:15 -0700 Subject: [PATCH 1/4] Added store call for time series group renames to update description --- .../cwms/cda/api/TimeSeriesGroupController.java | 13 +++++++++++++ .../cda/api/TimeSeriesGroupControllerTestIT.java | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java index 43f937582b..7c81c13d54 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java @@ -281,6 +281,12 @@ public void update(@NotNull Context ctx, @NotNull String oldGroupId) { boolean replaceAssignedTs = ctx.queryParamAsClass(REPLACE_ASSIGNED_TS, Boolean.class) .getOrDefault(false); TimeSeriesGroupDao timeSeriesGroupDao = new TimeSeriesGroupDao(dsl); + TimeSeriesGroup existingGroup = timeSeriesGroupDao.getTimeSeriesGroup(office, null, + null, group.getTimeSeriesCategory().getId(), oldGroupId); + if (!existingGroup.getDescription().equalsIgnoreCase(group.getDescription())) { + existingGroup = updateClearedFields(group, existingGroup); + timeSeriesGroupDao.create(existingGroup, false, false); + } if (!office.equalsIgnoreCase(CWMS_OFFICE) && !oldGroupId.equals(group.getId())) { timeSeriesGroupDao.renameTimeSeriesGroup(oldGroupId, group); } @@ -292,6 +298,13 @@ public void update(@NotNull Context ctx, @NotNull String oldGroupId) { } } + private TimeSeriesGroup updateClearedFields(TimeSeriesGroup groupBody, TimeSeriesGroup existingTimeSeriesGroup) { + return new TimeSeriesGroup(new TimeSeriesGroup(existingTimeSeriesGroup.getTimeSeriesCategory(), + existingTimeSeriesGroup.getOfficeId(), existingTimeSeriesGroup.getId(), groupBody.getDescription(), + existingTimeSeriesGroup.getSharedAliasId(), existingTimeSeriesGroup.getSharedRefTsId()), + existingTimeSeriesGroup.getAssignedTimeSeries()); + } + @OpenApi( description = "Deletes requested time series group", pathParams = { diff --git a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java index 8d79bf47e0..f25089aa7d 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java @@ -1009,7 +1009,7 @@ void test_rename_group(String format) throws Exception { .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_CREATED)); - TimeSeriesGroup newGroup = new TimeSeriesGroup(cat, officeId, "test_rename_group_new", "IntegrationTesting", + TimeSeriesGroup newGroup = new TimeSeriesGroup(cat, officeId, "test_rename_group_new", "Test group rename", "sharedTsAliasId2", timeSeriesId); String newGroupXml = Formats.format(contentType, newGroup); //Rename Group @@ -1050,7 +1050,7 @@ void test_rename_group(String format) throws Exception { .statusCode(is(HttpServletResponse.SC_OK)) .body("office-id", equalTo(newGroup.getOfficeId())) .body("id", equalTo(newGroup.getId())) - .body("description", equalTo(newGroup.getDescription())) + .body("description", equalTo("Test group rename")) .body("assigned-time-series[0].timeseries-id", equalTo(timeSeriesId)) .body("assigned-time-series[0].alias-id", equalTo("AliasId")) .body("assigned-time-series[0].ref-ts-id", equalTo(timeSeriesId)); From 07d46575359e53344db73b4dea9f04d6d44e97ce Mon Sep 17 00:00:00 2001 From: zack-rma Date: Thu, 30 Apr 2026 17:01:39 -0700 Subject: [PATCH 2/4] Updated assignment handling - removed unassignment call --- .../cda/api/TimeSeriesGroupController.java | 42 +++++++++--- .../api/TimeSeriesGroupControllerTestIT.java | 64 +++++++++++++------ 2 files changed, 76 insertions(+), 30 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java index 7c81c13d54..3b727a479f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java @@ -25,12 +25,36 @@ package cwms.cda.api; import static com.codahale.metrics.MetricRegistry.name; -import static cwms.cda.api.Controllers.*; +import static cwms.cda.api.Controllers.CASCADE_DELETE; +import static cwms.cda.api.Controllers.CATEGORY_ID; +import static cwms.cda.api.Controllers.CATEGORY_OFFICE_ID; +import static cwms.cda.api.Controllers.CREATE; +import static cwms.cda.api.Controllers.CWMS_OFFICE; +import static cwms.cda.api.Controllers.FAIL_IF_EXISTS; +import static cwms.cda.api.Controllers.GET_ALL; +import static cwms.cda.api.Controllers.GET_ONE; +import static cwms.cda.api.Controllers.GROUP_ID; +import static cwms.cda.api.Controllers.GROUP_OFFICE_ID; +import static cwms.cda.api.Controllers.IGNORE_NULLS; +import static cwms.cda.api.Controllers.INCLUDE_ASSIGNED; +import static cwms.cda.api.Controllers.OFFICE; +import static cwms.cda.api.Controllers.REPLACE_ASSIGNED_TS; +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_404; +import static cwms.cda.api.Controllers.STATUS_501; +import static cwms.cda.api.Controllers.TIMESERIES_CATEGORY_LIKE; +import static cwms.cda.api.Controllers.TIMESERIES_GROUP_LIKE; +import static cwms.cda.api.Controllers.UPDATE; +import static cwms.cda.api.Controllers.queryParamAsClass; +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 com.google.common.flogger.FluentLogger; import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.TimeSeriesGroupDao; import cwms.cda.data.dto.TimeSeriesGroup; @@ -47,7 +71,6 @@ import io.javalin.plugin.openapi.annotations.OpenApiRequestBody; import io.javalin.plugin.openapi.annotations.OpenApiResponse; import java.util.List; -import com.google.common.flogger.FluentLogger; import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; @@ -283,26 +306,24 @@ public void update(@NotNull Context ctx, @NotNull String oldGroupId) { TimeSeriesGroupDao timeSeriesGroupDao = new TimeSeriesGroupDao(dsl); TimeSeriesGroup existingGroup = timeSeriesGroupDao.getTimeSeriesGroup(office, null, null, group.getTimeSeriesCategory().getId(), oldGroupId); - if (!existingGroup.getDescription().equalsIgnoreCase(group.getDescription())) { - existingGroup = updateClearedFields(group, existingGroup); + if (!existingGroup.getDescription().equalsIgnoreCase(group.getDescription()) || replaceAssignedTs) { + existingGroup = updateClearedFields(group, existingGroup, replaceAssignedTs); timeSeriesGroupDao.create(existingGroup, false, false); } if (!office.equalsIgnoreCase(CWMS_OFFICE) && !oldGroupId.equals(group.getId())) { timeSeriesGroupDao.renameTimeSeriesGroup(oldGroupId, group); } - if (replaceAssignedTs) { - timeSeriesGroupDao.unassignForOffice(group.getTimeSeriesCategory().getId(), group.getId(), group.getOfficeId(), office); - } timeSeriesGroupDao.assignTs(group, office); ctx.status(HttpServletResponse.SC_OK); } } - private TimeSeriesGroup updateClearedFields(TimeSeriesGroup groupBody, TimeSeriesGroup existingTimeSeriesGroup) { + private TimeSeriesGroup updateClearedFields(TimeSeriesGroup groupBody, + TimeSeriesGroup existingTimeSeriesGroup, boolean replaceAssignedTs) { return new TimeSeriesGroup(new TimeSeriesGroup(existingTimeSeriesGroup.getTimeSeriesCategory(), existingTimeSeriesGroup.getOfficeId(), existingTimeSeriesGroup.getId(), groupBody.getDescription(), existingTimeSeriesGroup.getSharedAliasId(), existingTimeSeriesGroup.getSharedRefTsId()), - existingTimeSeriesGroup.getAssignedTimeSeries()); + replaceAssignedTs ? groupBody.getAssignedTimeSeries() : existingTimeSeriesGroup.getAssignedTimeSeries()); } @OpenApi( @@ -316,7 +337,8 @@ private TimeSeriesGroup updateClearedFields(TimeSeriesGroup groupBody, TimeSerie @OpenApiParam(name = OFFICE, required = true, description = "Specifies the " + "owning office of the time series group to be deleted"), @OpenApiParam(name = CASCADE_DELETE, type = Boolean.class, - description = "Specifies whether to unassign time series in this group before deleting. Default: false"), + description = "Specifies whether to unassign time series in this group before deleting. " + + "Default: false"), }, method = HttpMethod.DELETE, tags = {TAG} diff --git a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java index 8e20a65fbb..9ffd611348 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java @@ -145,7 +145,7 @@ void clear_data() throws Exception { } catch (NotFoundException e) { LOGGER.atConfig().withCause(e).log("Group not found"); } catch (DataAccessException e) { - LOGGER.atInfo().withCause(e).log("Failed to unassign ts from %s that are in group owned by:%s", assignOffice, group.getOfficeId()); + LOGGER.atInfo().withCause(e).log("Failed to unassign ts from %s that are in group owned by %s", assignOffice, group.getOfficeId()); } } cwmsgroupsToSPKUnassign.clear(); @@ -1222,6 +1222,26 @@ void test_add_assigned_locs(String format) { .assertThat() .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_OK)); + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(format) + .contentType(Formats.JSON) + .queryParam(OFFICE, officeId) + .queryParam(CATEGORY_OFFICE_ID, officeId) + .queryParam(GROUP_OFFICE_ID, officeId) + .queryParam(CATEGORY_ID, group.getTimeSeriesCategory().getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/timeseries/group/" + group.getId()) + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_OK)) + .body("office-id", equalTo(group.getOfficeId())) + .body("id", equalTo(group.getId())) + .body("description", equalTo(group.getDescription())) + .body("assigned-time-series.size()", equalTo(0)); //Delete Group given() .log().ifValidationFails(LogDetail.ALL,true) @@ -1519,16 +1539,18 @@ void test_patch_district_permission(String format) throws Exception { // Create Category given() - .log().ifValidationFails(LogDetail.ALL, true) - .accept(Formats.JSON) - .contentType(Formats.JSON) - .body(json) - .header("Authorization", user.toHeaderValue()) - .when() - .post("/timeseries/category") - .then() - .statusCode(anyOf(is(HttpServletResponse.SC_CREATED), is(HttpServletResponse.SC_CONFLICT))) - ; + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(json) + .header("Authorization", user.toHeaderValue()) + .when() + .post("/timeseries/category") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(anyOf(is(HttpServletResponse.SC_CREATED), is(HttpServletResponse.SC_CONFLICT))) + ; TimeSeriesGroup districtGroup = new TimeSeriesGroup(category, CWMS_OFFICE, "Default", "All Time Series", null, null); @@ -1539,15 +1561,17 @@ void test_patch_district_permission(String format) throws Exception { // Create Group given() - .log().ifValidationFails(LogDetail.ALL, true) - .accept(Formats.JSON) - .contentType(Formats.JSON) - .body(json1) - .header("Authorization", user.toHeaderValue()) - .when() - .post("/timeseries/group") - .then() - .statusCode(anyOf(is(HttpServletResponse.SC_CREATED), is(HttpServletResponse.SC_CONFLICT))) + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(json1) + .header("Authorization", user.toHeaderValue()) + .when() + .post("/timeseries/group") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(anyOf(is(HttpServletResponse.SC_CREATED), is(HttpServletResponse.SC_CONFLICT))) ; AssignedTimeSeries assignedTimeSeries = new AssignedTimeSeries(officeId, tsId, null, null, null); From 608185d70ef2940d26b32a9db38212af1b71569d Mon Sep 17 00:00:00 2001 From: zack-rma Date: Mon, 4 May 2026 10:45:10 -0700 Subject: [PATCH 3/4] Reintroduced unassignment of ts from groups --- .../java/cwms/cda/api/TimeSeriesGroupController.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java index 3b727a479f..3e493b1680 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java @@ -306,24 +306,27 @@ public void update(@NotNull Context ctx, @NotNull String oldGroupId) { TimeSeriesGroupDao timeSeriesGroupDao = new TimeSeriesGroupDao(dsl); TimeSeriesGroup existingGroup = timeSeriesGroupDao.getTimeSeriesGroup(office, null, null, group.getTimeSeriesCategory().getId(), oldGroupId); - if (!existingGroup.getDescription().equalsIgnoreCase(group.getDescription()) || replaceAssignedTs) { - existingGroup = updateClearedFields(group, existingGroup, replaceAssignedTs); + if (!existingGroup.getDescription().equalsIgnoreCase(group.getDescription())) { + existingGroup = updateClearedFields(group, existingGroup); timeSeriesGroupDao.create(existingGroup, false, false); } if (!office.equalsIgnoreCase(CWMS_OFFICE) && !oldGroupId.equals(group.getId())) { timeSeriesGroupDao.renameTimeSeriesGroup(oldGroupId, group); } + if (replaceAssignedTs) { + timeSeriesGroupDao.unassignAll(group.getTimeSeriesCategory().getId(), group.getId(), office); + } timeSeriesGroupDao.assignTs(group, office); ctx.status(HttpServletResponse.SC_OK); } } private TimeSeriesGroup updateClearedFields(TimeSeriesGroup groupBody, - TimeSeriesGroup existingTimeSeriesGroup, boolean replaceAssignedTs) { + TimeSeriesGroup existingTimeSeriesGroup) { return new TimeSeriesGroup(new TimeSeriesGroup(existingTimeSeriesGroup.getTimeSeriesCategory(), existingTimeSeriesGroup.getOfficeId(), existingTimeSeriesGroup.getId(), groupBody.getDescription(), existingTimeSeriesGroup.getSharedAliasId(), existingTimeSeriesGroup.getSharedRefTsId()), - replaceAssignedTs ? groupBody.getAssignedTimeSeries() : existingTimeSeriesGroup.getAssignedTimeSeries()); + existingTimeSeriesGroup.getAssignedTimeSeries()); } @OpenApi( From a54929c000f99a2538d96aff311d3bfcbf8c350b Mon Sep 17 00:00:00 2001 From: zack-rma Date: Tue, 5 May 2026 08:50:07 -0700 Subject: [PATCH 4/4] Swap unassign call. Update office used. --- .../java/cwms/cda/api/TimeSeriesGroupController.java | 3 ++- .../java/cwms/cda/data/dao/TimeSeriesGroupDao.java | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java index 3e493b1680..a2cbf7bf2d 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java @@ -314,7 +314,8 @@ public void update(@NotNull Context ctx, @NotNull String oldGroupId) { timeSeriesGroupDao.renameTimeSeriesGroup(oldGroupId, group); } if (replaceAssignedTs) { - timeSeriesGroupDao.unassignAll(group.getTimeSeriesCategory().getId(), group.getId(), office); + timeSeriesGroupDao.unassignForOffice(group.getTimeSeriesCategory().getId(), group.getId(), + group.getOfficeId(), office); } timeSeriesGroupDao.assignTs(group, office); ctx.status(HttpServletResponse.SC_OK); diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java index 7f455b48f8..e582b09c08 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java @@ -373,9 +373,9 @@ private void deleteViaUnassign(DSLContext dslContext, String categoryId, String public void unassignAll(String categoryId, String groupId, String office) { - dsl.transaction((Configuration config) -> { - unassignAll(config, categoryId, groupId, office); - }); + dsl.transaction((Configuration config) -> + unassignAll(config, categoryId, groupId, office) + ); } // This may not be that useful in practice. Typically groups either below to an office like SPK or to CWMS. @@ -402,12 +402,13 @@ private void unassignAll(Configuration config, String categoryId, String groupId public void unassignForOffice( String categoryId, String groupId, String office, String assignmentOffice) { connection(dsl, conn -> { - DSLContext dslContext = getDslContext(conn, office); + DSLContext dslContext = getDslContext(conn, assignmentOffice); unassignForOffice(dslContext.configuration(), categoryId, groupId, office, assignmentOffice); }); } - public static void unassignForOffice(Configuration config, String categoryId, String groupId, String office, String assignmentOffice) { + public static void unassignForOffice(Configuration config, String categoryId, String groupId, + String office, String assignmentOffice) { if (office != null && !"CWMS".equals(office)) { CWMS_ENV_PACKAGE.call_SET_SESSION_OFFICE_ID(config, assignmentOffice); }