Skip to content

Commit 67fa3ec

Browse files
zack-rmaMikeNeilson
authored andcommitted
CDA-92: Fix to resolve TimeSeriesGroup DAO serializing AssignedTimeSeries nulls (#1566)
Fix to resolve TimeSeriesGroup DAO serializing AssignedTimeSeries null attributes as strings. Includes integration test and serialization unit test. Resolves #1564 (cherry picked from commit 576bd7e)
1 parent 06123bb commit 67fa3ec

3 files changed

Lines changed: 227 additions & 14 deletions

File tree

cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,24 @@
2525
package cwms.cda.data.dao;
2626

2727
import static com.google.common.flogger.LazyArgs.lazy;
28-
2928
import static java.util.stream.Collectors.toList;
3029

30+
import com.google.common.flogger.FluentLogger;
3131
import cwms.cda.data.dto.AssignedTimeSeries;
3232
import cwms.cda.data.dto.TimeSeriesCategory;
3333
import cwms.cda.data.dto.TimeSeriesGroup;
3434
import java.math.BigDecimal;
3535
import java.util.List;
36-
import com.google.common.flogger.FluentLogger;
3736
import org.jetbrains.annotations.NotNull;
38-
import org.jooq.*;
37+
import org.jooq.Condition;
38+
import org.jooq.Configuration;
39+
import org.jooq.DSLContext;
40+
import org.jooq.Record5;
41+
import org.jooq.Record8;
42+
import org.jooq.Record9;
43+
import org.jooq.RecordMapper;
44+
import org.jooq.SelectConditionStep;
45+
import org.jooq.SelectSeekStep4;
3946
import org.jooq.conf.ParamType;
4047
import org.jooq.impl.DSL;
4148
import usace.cwms.db.jooq.codegen.packages.CWMS_TS_PACKAGE;
@@ -189,11 +196,11 @@ private AssignedTimeSeries buildAssignedTimeSeries(Record5<String, String, BigDe
189196
AssignedTimeSeries retval = null;
190197

191198
if (multisetRecord != null) {
192-
String timeseriesId = String.valueOf(multisetRecord.get(0));
193-
String officeId = String.valueOf(multisetRecord.get(1));
194-
BigDecimal attrBD = (BigDecimal) multisetRecord.get(2);
195-
String aliasId = String.valueOf(multisetRecord.get(3));
196-
String refTsId = String.valueOf(multisetRecord.get(4));
199+
String timeseriesId = multisetRecord.get(0, String.class);
200+
String officeId = multisetRecord.get(1, String.class);
201+
BigDecimal attrBD = multisetRecord.get(2, BigDecimal.class);
202+
String aliasId = multisetRecord.get(3, String.class);
203+
String refTsId = multisetRecord.get(4, String.class);
197204

198205
Integer attr = null;
199206
if (attrBD != null) {

cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java

Lines changed: 181 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@
7878
@Tag("integration")
7979
final class TimeSeriesGroupControllerTestIT extends DataApiTestIT {
8080

81-
private List<TimeSeriesCategory> categoriesToCleanup = new ArrayList<>();
82-
private List<TimeSeriesGroup> groupsToCleanup = new ArrayList<>();
83-
private List<TimeSeries> timeSeriesToCleanup = new ArrayList<>();
81+
private final List<TimeSeriesCategory> categoriesToCleanup = new ArrayList<>();
82+
private final List<TimeSeriesGroup> groupsToCleanup = new ArrayList<>();
83+
private final List<TimeSeries> timeSeriesToCleanup = new ArrayList<>();
8484
private static final FluentLogger LOGGER = FluentLogger.forEnclosingClass();
8585
TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL;
8686
TestAccounts.KeyUser user2 = TestAccounts.KeyUser.SWT_NORMAL;
@@ -1671,4 +1671,182 @@ void testRetrievalTiming() {
16711671
.body("assigned-time-series.size()", greaterThan(0))
16721672
.time(lessThan(500L)); // should be pretty quick, under 0.5 seconds. Old query was ~3 seconds
16731673
}
1674+
1675+
@ParameterizedTest
1676+
@ValueSource(strings = {Formats.JSON, Formats.DEFAULT})
1677+
void test_create_read_delete_null_attributes(String format) throws Exception {
1678+
String officeId = user.getOperatingOffice();
1679+
String timeSeriesId = "Alder Springs.Precip-Cumulative.Inst.1Day.0.cda-attr";
1680+
createLocation(timeSeriesId.split("\\.")[0],true, officeId);
1681+
TimeSeriesCategory cat = new TimeSeriesCategory(officeId, "test_attr", "NullTesting");
1682+
TimeSeriesGroup group = new TimeSeriesGroup(cat, officeId, "test_attr", "NullTesting",
1683+
null, null);
1684+
List<AssignedTimeSeries> assignedTimeSeries = group.getAssignedTimeSeries();
1685+
1686+
assignedTimeSeries.add(new AssignedTimeSeries(officeId,timeSeriesId, null, null, 0));
1687+
ContentType contentType = Formats.parseHeader(Formats.JSON, TimeSeriesCategory.class);
1688+
String categoryXml = Formats.format(contentType, cat);
1689+
String groupXml = Formats.format(contentType, group);
1690+
1691+
//Create Category
1692+
given()
1693+
.log().ifValidationFails(LogDetail.ALL,true)
1694+
.accept(format)
1695+
.contentType(Formats.JSON)
1696+
.body(categoryXml)
1697+
.header("Authorization", user.toHeaderValue())
1698+
.queryParam(OFFICE, officeId)
1699+
.queryParam(FAIL_IF_EXISTS, false)
1700+
.when()
1701+
.redirects().follow(true)
1702+
.redirects().max(3)
1703+
.post("/timeseries/category")
1704+
.then()
1705+
.log().ifValidationFails(LogDetail.ALL,true)
1706+
.assertThat()
1707+
.statusCode(is(HttpServletResponse.SC_CREATED));
1708+
1709+
// insert the time series
1710+
createTimeseries(officeId, timeSeriesId, 0);
1711+
1712+
//Create Group
1713+
given()
1714+
.log().ifValidationFails(LogDetail.ALL,true)
1715+
.accept(format)
1716+
.contentType(Formats.JSON)
1717+
.body(groupXml)
1718+
.header("Authorization", user.toHeaderValue())
1719+
.queryParam(FAIL_IF_EXISTS, false)
1720+
.when()
1721+
.redirects().follow(true)
1722+
.redirects().max(3)
1723+
.post("/timeseries/group")
1724+
.then()
1725+
.log().ifValidationFails(LogDetail.ALL,true)
1726+
.assertThat()
1727+
.statusCode(is(HttpServletResponse.SC_CREATED));
1728+
1729+
//Read
1730+
var tsGroup = given()
1731+
.log().ifValidationFails(LogDetail.ALL,true)
1732+
.accept(format)
1733+
.contentType(Formats.JSON)
1734+
.queryParam(OFFICE, officeId)
1735+
.queryParam(CATEGORY_OFFICE_ID, officeId)
1736+
.queryParam(GROUP_OFFICE_ID, officeId)
1737+
.queryParam(CATEGORY_ID, group.getTimeSeriesCategory().getId())
1738+
.when()
1739+
.redirects().follow(true)
1740+
.redirects().max(3)
1741+
.get("/timeseries/group/" + group.getId())
1742+
.then()
1743+
.log().ifValidationFails(LogDetail.ALL,true)
1744+
.assertThat()
1745+
.statusCode(is(HttpServletResponse.SC_OK))
1746+
.body("office-id", equalTo(group.getOfficeId()))
1747+
.body("id", equalTo(group.getId()))
1748+
.body("description", equalTo(group.getDescription()))
1749+
.body("assigned-time-series[0].timeseries-id", equalTo(timeSeriesId))
1750+
.extract();
1751+
1752+
var body = tsGroup.body().jsonPath().getList("assigned-time-series");
1753+
for (Object o : body) {
1754+
String content = o.toString();
1755+
assertFalse(content.contains("ref-ts-id"));
1756+
assertFalse(content.contains("ts-code"));
1757+
assertFalse(content.contains("alias-id"));
1758+
assertFalse(content.contains("null"));
1759+
}
1760+
1761+
//Clear Assigned TS
1762+
group.getAssignedTimeSeries().clear();
1763+
groupXml = Formats.format(contentType, group);
1764+
given()
1765+
.log().ifValidationFails(LogDetail.ALL,true)
1766+
.accept(format)
1767+
.contentType(Formats.JSON)
1768+
.body(groupXml)
1769+
.header("Authorization", user.toHeaderValue())
1770+
.queryParam(CATEGORY_ID, group.getTimeSeriesCategory().getId())
1771+
.queryParam(REPLACE_ASSIGNED_TS, true)
1772+
.queryParam(OFFICE, group.getOfficeId())
1773+
.when()
1774+
.redirects().follow(true)
1775+
.redirects().max(3)
1776+
.patch("/timeseries/group/"+ group.getId())
1777+
.then()
1778+
.log().ifValidationFails(LogDetail.ALL,true)
1779+
.assertThat()
1780+
.statusCode(is(HttpServletResponse.SC_OK));
1781+
1782+
//Delete timeseries
1783+
given()
1784+
.log().ifValidationFails(LogDetail.ALL, true)
1785+
.accept(format)
1786+
.header("Authorization", user.toHeaderValue())
1787+
.queryParam(OFFICE, officeId)
1788+
.queryParam(BEGIN, "2025-05-08T11:00:00+00:00")
1789+
.queryParam(END, "2025-05-19T11:00:00+00:00")
1790+
.queryParam("start-time-inclusive", "true")
1791+
.queryParam("end-time-inclusive", "true")
1792+
.queryParam("override-protection", "true")
1793+
.when()
1794+
.redirects().follow(true)
1795+
.redirects().max(3)
1796+
.delete("/timeseries/" + timeSeriesId)
1797+
.then()
1798+
.log().ifValidationFails(LogDetail.ALL, true)
1799+
.assertThat()
1800+
.statusCode(is(HttpServletResponse.SC_OK));
1801+
1802+
//Delete Group
1803+
given()
1804+
.log().ifValidationFails(LogDetail.ALL,true)
1805+
.accept(format)
1806+
.contentType(Formats.JSON)
1807+
.header("Authorization", user.toHeaderValue())
1808+
.queryParam(OFFICE, officeId)
1809+
.queryParam(CATEGORY_ID, cat.getId())
1810+
.when()
1811+
.redirects().follow(true)
1812+
.redirects().max(3)
1813+
.delete("/timeseries/group/" + group.getId())
1814+
.then()
1815+
.log().ifValidationFails(LogDetail.ALL,true)
1816+
.assertThat()
1817+
.statusCode(is(HttpServletResponse.SC_NO_CONTENT));
1818+
1819+
//Read Empty
1820+
given()
1821+
.log().ifValidationFails(LogDetail.ALL,true)
1822+
.accept(format)
1823+
.contentType(Formats.JSON)
1824+
.queryParam(OFFICE, officeId)
1825+
.queryParam(GROUP_OFFICE_ID, officeId)
1826+
.queryParam(CATEGORY_OFFICE_ID, officeId)
1827+
.when()
1828+
.redirects().follow(true)
1829+
.redirects().max(3)
1830+
.get("/timeseries/group/" + group.getId())
1831+
.then()
1832+
.log().ifValidationFails(LogDetail.ALL,true)
1833+
.assertThat()
1834+
.statusCode(is(HttpServletResponse.SC_NOT_FOUND));
1835+
1836+
//Delete Category
1837+
given()
1838+
.log().ifValidationFails(LogDetail.ALL,true)
1839+
.accept(format)
1840+
.contentType(Formats.JSON)
1841+
.header("Authorization", user.toHeaderValue())
1842+
.queryParam(OFFICE, officeId)
1843+
.when()
1844+
.redirects().follow(true)
1845+
.redirects().max(3)
1846+
.delete("/timeseries/category/" + group.getTimeSeriesCategory().getId())
1847+
.then()
1848+
.log().ifValidationFails(LogDetail.ALL,true)
1849+
.assertThat()
1850+
.statusCode(is(HttpServletResponse.SC_NO_CONTENT));
1851+
}
16741852
}

cwms-data-api/src/test/java/cwms/cda/data/dto/TimeSeriesGroupTest.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package cwms.cda.data.dto;
22

3+
import java.util.ArrayList;
4+
import java.util.List;
35
import org.junit.jupiter.api.Test;
46

57
import cwms.cda.formatters.ContentType;
68
import cwms.cda.formatters.Formats;
79

10+
import static org.junit.jupiter.api.Assertions.assertFalse;
811
import static org.junit.jupiter.api.Assertions.assertNotNull;
912
import static org.junit.jupiter.api.Assertions.assertTrue;
1013

@@ -30,17 +33,42 @@ void test_serialize_json(){
3033
assertTrue(result.contains("grpSharedRefTsId"));
3134
}
3235

36+
@Test
37+
void test_serialize_with_nulls() {
38+
TimeSeriesGroup group = buildTimeSeriesGroup();
39+
List<AssignedTimeSeries> assignedTimeSeries = new ArrayList<>();
40+
AssignedTimeSeries timeSeries = new AssignedTimeSeries("SPK",
41+
"BIG MUDDY.Elev.Total.1Day.1Day.CWMS", null, null, 0);
42+
assignedTimeSeries.add(timeSeries);
43+
TimeSeriesGroup groupWithNulls = new TimeSeriesGroup(group, assignedTimeSeries);
44+
45+
ContentType contentType = Formats.parseHeader(Formats.JSON, TimeSeriesGroup.class);
46+
String result = Formats.format(contentType, groupWithNulls);
47+
assertNotNull(result);
48+
49+
assertTrue(result.contains("catOfficeId"));
50+
assertTrue(result.contains("catId"));
51+
assertTrue(result.contains("catDesc"));
52+
53+
assertTrue(result.contains("grpOfficeId"));
54+
assertTrue(result.contains("grpId"));
55+
assertTrue(result.contains("grpDesc"));
56+
assertTrue(result.contains("grpSharedTsAliasId"));
57+
assertTrue(result.contains("grpSharedRefTsId"));
58+
59+
assertFalse(result.contains("null"));
60+
}
61+
3362

3463
private TimeSeriesGroup buildTimeSeriesGroup()
3564
{
3665
TimeSeriesCategory category = new TimeSeriesCategory(
3766
"catOfficeId", "catId", "catDesc"
3867
);
39-
TimeSeriesGroup retval = new TimeSeriesGroup(category,
68+
69+
return new TimeSeriesGroup(category,
4070
"grpOfficeId", "grpId", "grpDesc",
4171
"grpSharedTsAliasId", "grpSharedRefTsId"
4272
);
43-
44-
return retval;
4573
}
4674
}

0 commit comments

Comments
 (0)