From a03d54a3af91f5ffe25183539a42787a0135e8c5 Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Tue, 4 Aug 2026 19:10:56 +0000 Subject: [PATCH 01/13] Correct format lookup logic. Always map primary type to itself in ContentTypeAliasMap. --- .../java/cwms/cda/formatters/ContentTypeAliasMap.java | 2 +- .../src/main/java/cwms/cda/formatters/Formats.java | 8 -------- .../src/test/java/cwms/cda/formatters/FormatsTest.java | 8 +++++++- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/formatters/ContentTypeAliasMap.java b/cwms-data-api/src/main/java/cwms/cda/formatters/ContentTypeAliasMap.java index ec1ae7bc7..f839cb558 100644 --- a/cwms-data-api/src/main/java/cwms/cda/formatters/ContentTypeAliasMap.java +++ b/cwms-data-api/src/main/java/cwms/cda/formatters/ContentTypeAliasMap.java @@ -24,7 +24,7 @@ private ContentTypeAliasMap(Class dtoClass) { FormattableWith[] formats = dtoClass.getAnnotationsByType(FormattableWith.class); for (FormattableWith format : formats) { ContentType type = new ContentType(format.contentType()); - + contentTypeMap.put(type, type); // we can always map to our self. for (String alias : format.aliases()) { contentTypeMap.put(new ContentType(alias), type); } diff --git a/cwms-data-api/src/main/java/cwms/cda/formatters/Formats.java b/cwms-data-api/src/main/java/cwms/cda/formatters/Formats.java index 107372163..44f4ee669 100644 --- a/cwms-data-api/src/main/java/cwms/cda/formatters/Formats.java +++ b/cwms-data-api/src/main/java/cwms/cda/formatters/Formats.java @@ -412,14 +412,6 @@ public static ContentType parseQueryParam(String queryParam, Class Formats.parseHeader("abc", Catalog.class)); assertThrows(FormattingException.class, () -> Formats.parseHeader("abc,def", Catalog.class)); + contentType = Formats.parseHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", RecentValue.class); + assertEquals("application/json", contentType.getType()); } - @ParameterizedTest @EnumSource(ParseQueryOrParamTest.class) void test_header_or_query_parm(ParseQueryOrParamTest test) { From 10773817aa5b14e952b7147efba755d80e49102f Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Tue, 4 Aug 2026 20:05:11 +0000 Subject: [PATCH 02/13] Correct test and format aliases. --- .../cwms/cda/data/dto/location/kind/Turbine.java | 2 +- .../test/java/cwms/cda/formatters/FormatsTest.java | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/location/kind/Turbine.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/location/kind/Turbine.java index 5d1e0af0d..ce5c6402b 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/location/kind/Turbine.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/location/kind/Turbine.java @@ -35,7 +35,7 @@ import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.json.JsonV1; -@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class, aliases = {Formats.DEFAULT, Formats.JSONV1}) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class, aliases = {Formats.DEFAULT, Formats.JSON}) @JsonDeserialize(builder = Turbine.Builder.class) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) diff --git a/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java b/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java index 115a0e37a..d91a66a79 100644 --- a/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/formatters/FormatsTest.java @@ -94,17 +94,9 @@ void testParseHeaderAndQueryParmXml() { Map parameters = contentType.getParameters(); assertTrue(parameters == null || parameters.isEmpty()); - - contentType = Formats.parseHeaderAndQueryParm("application/xml;version=2", null, Catalog.class); - - assertNotNull(contentType); - assertEquals("application/xml", contentType.getType()); - parameters = contentType.getParameters(); - assertNotNull(parameters); - assertFalse(parameters.isEmpty()); - assertTrue(parameters.containsKey("version")); - assertEquals("2", parameters.get("version")); - + /** xml;version=2 is not a supported format of Catalog */ + assertThrows(UnsupportedFormatException.class, + () -> Formats.parseHeaderAndQueryParm("application/xml;version=2", null, Catalog.class)); } @Test From 7662d8b984175f29a2c7a73708a7c1af69ff6ea0 Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Tue, 4 Aug 2026 21:59:15 +0000 Subject: [PATCH 03/13] Various corrections. --- cda-gui/package-lock.json | 55 +++++-------------- .../cwms/cda/api/rating/RatingController.java | 3 +- .../cwms/cda/data/dto/rating/Ratings.java | 12 ++++ .../java/cwms/cda/api/BasinControllerIT.java | 16 +++--- .../fixtures/CwmsDataApiSetupCallback.java | 19 ++++++- 5 files changed, 53 insertions(+), 52 deletions(-) create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java diff --git a/cda-gui/package-lock.json b/cda-gui/package-lock.json index cc71620c0..ed481ac1d 100644 --- a/cda-gui/package-lock.json +++ b/cda-gui/package-lock.json @@ -82,6 +82,7 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1200,9 +1201,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1217,9 +1215,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1234,9 +1229,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1251,9 +1243,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1268,9 +1257,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1285,9 +1271,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1302,9 +1285,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1319,9 +1299,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1336,9 +1313,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1353,9 +1327,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1370,9 +1341,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1387,9 +1355,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1404,9 +1369,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1540,6 +1502,7 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.1.tgz", "integrity": "sha512-ZnONUuQKJe1bJMStXUL1s5uKN9FcfC28j5cK+iDZcdSHtUv1wtin1cGc/Oewhf2Oc4eKY7lggtpvT/AbMmhHew==", "license": "MIT", + "peer": true, "dependencies": { "@tanstack/query-core": "5.101.1" }, @@ -1649,6 +1612,7 @@ "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -1703,6 +1667,7 @@ "resolved": "https://registry.npmjs.org/@usace/groundwork/-/groundwork-3.15.6.tgz", "integrity": "sha512-0Q37NW/wsuST4Y70sPNbR4hfTycyd7MwUIcfIhYvAu1lwxpQTXSAmDPGnwlmCrmHQd4hSTmCB44IHFwyjWHM4A==", "license": "MIT", + "peer": true, "dependencies": { "@headlessui/react": "^2.2.0", "@tailwindcss/forms": "^0.5.9", @@ -1754,6 +1719,7 @@ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2162,6 +2128,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", @@ -2936,6 +2903,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4256,6 +4224,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -5138,6 +5107,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -5402,6 +5372,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5435,6 +5406,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5574,7 +5546,8 @@ "version": "28.1.0", "resolved": "https://registry.npmjs.org/redux-bundler/-/redux-bundler-28.1.0.tgz", "integrity": "sha512-dbq0J0Sm0ctK9ZUh73JR/6J937qzNvkKEHXtHv7p2Hl/9sS+xyRQReWjuju8LHopYFiG+U0Lg9XZM1hPp1WkHg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-bundler-hook": { "version": "1.0.3", @@ -6359,6 +6332,7 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "license": "MIT", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -6696,6 +6670,7 @@ "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", 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 9aa259318..9225df4c8 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 @@ -67,6 +67,7 @@ import cwms.cda.data.dto.CwmsDTOBase; import cwms.cda.data.dto.StatusResponse; import cwms.cda.data.dto.VerticalDatumInfo; +import cwms.cda.data.dto.rating.Ratings; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; @@ -184,7 +185,7 @@ private String deserializeRatingSet(Context ctx, boolean storeTemplate) throws I String formatHeader = ctx.req.getContentType(); //Using placeholder CwmsDTOBase.class since we do not have a RatingSet DTO //The contentType will match against the standard listing of Formats constants - ContentType contentType = Formats.parseHeader(formatHeader, CwmsDTOBase.class); + ContentType contentType = Formats.parseHeader(formatHeader, Ratings.class); String body = ctx.body(); return deserializeRatingSet(body, contentType.getType(), storeTemplate); } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java new file mode 100644 index 000000000..a527ccad6 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java @@ -0,0 +1,12 @@ +package cwms.cda.data.dto.rating; + +import cwms.cda.data.dto.CwmsDTOBase; +import cwms.cda.formatters.Formats; +import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.xml.XMLv2; + +@FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.DEFAULT, Formats.XML}) +@FormattableWith(contentType = Formats.JSONV2, formatter = XMLv2.class, aliases = {Formats.JSON}) +public class Ratings extends CwmsDTOBase { + /** marker interface for ContentType Annotations */ +} diff --git a/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java index d6e6e7b50..206924274 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java @@ -202,7 +202,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -227,7 +227,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -250,7 +250,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -274,7 +274,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id.name", equalTo(BASIN.getBasinId().getName())) .body("basin-id.office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("sort-order", equalTo(BASIN.getSortOrder())) .body("area-unit", equalTo(BASIN.getAreaUnit())) .body("total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -432,7 +432,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -457,7 +457,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -480,7 +480,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -504,7 +504,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) diff --git a/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java b/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java index 8ee280ded..6e2be0a16 100644 --- a/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java +++ b/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java @@ -32,6 +32,7 @@ import fixtures.tomcat.SingleSignOnWrapper; import helpers.TsRandomSampler; import io.restassured.RestAssured; +import io.restassured.config.EncoderConfig; import io.restassured.config.JsonConfig; import io.restassured.filter.log.LogDetail; import io.restassured.path.json.config.JsonPathConfig; @@ -179,11 +180,23 @@ public void beforeAll(ExtensionContext context) throws Exception { RestAssured.baseURI=CwmsDataApiSetupCallback.httpUrl(); RestAssured.port = CwmsDataApiSetupCallback.httpPort(); RestAssured.basePath = System.getProperty("warContext"); - // we only use doubles - RestAssured.config() + // actually assign the new config to the global configuration. just running this here without + // the assignment apparently does nothing. + RestAssured.config = RestAssured.config() + // we only use doubles .jsonConfig( JsonConfig.jsonConfig() - .numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)); + .numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)) + // our content type processing is a bit more picky now. + // I also don't recal seeing any default COntent-Type or Accept header + // defaults from browsers that include this much. + // if we start seeing it we need to add explicity @FormattableWith annotations + // per character as that is a distinct content-type. + .encoderConfig( + EncoderConfig.encoderConfig() + .appendDefaultContentCharsetToContentTypeIfUndefined( + false + )); healthCheck(); } } From 3c861e619199b1929a1ec765178ad3f62bc8607e Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 17:04:59 +0000 Subject: [PATCH 04/13] Basin tests back to normal. --- .../cda/data/dto/basinconnectivity/Basin.java | 2 ++ .../java/cwms/cda/api/BasinControllerIT.java | 16 ++++++++-------- .../java/fixtures/CwmsDataApiSetupCallback.java | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/basinconnectivity/Basin.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/basinconnectivity/Basin.java index fd5442079..f432e9ab1 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/basinconnectivity/Basin.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/basinconnectivity/Basin.java @@ -2,6 +2,7 @@ import cwms.cda.data.dto.CwmsDTO; import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.NamedPgJsonFormatter; import cwms.cda.formatters.Formats; import cwms.cda.formatters.json.PgJsonFormatter; @@ -9,6 +10,7 @@ @FormattableWith(contentType = Formats.NAMED_PGJSON, formatter = NamedPgJsonFormatter.class, aliases = {Formats.DEFAULT, Formats.JSON}) @FormattableWith(contentType = Formats.PGJSON, formatter = PgJsonFormatter.class) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) public final class Basin extends CwmsDTO { private final String basinName; diff --git a/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java index 206924274..d6e6e7b50 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/BasinControllerIT.java @@ -202,7 +202,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -227,7 +227,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -250,7 +250,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -274,7 +274,7 @@ void test_get_create_delete() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id.name", equalTo(BASIN.getBasinId().getName())) .body("basin-id.office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("sort-order", equalTo(BASIN.getSortOrder())) + .body("sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("area-unit", equalTo(BASIN.getAreaUnit())) .body("total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -432,7 +432,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -457,7 +457,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -480,7 +480,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) @@ -504,7 +504,7 @@ void test_get_all() { .statusCode(is(HttpServletResponse.SC_OK)) .body("basin-id[0].name", equalTo(BASIN.getBasinId().getName())) .body("basin-id[0].office-id", equalTo(BASIN.getBasinId().getOfficeId())) - .body("[0].sort-order", equalTo(BASIN.getSortOrder())) + .body("[0].sort-order", equalTo(BASIN.getSortOrder().floatValue())) .body("[0].area-unit", equalTo(BASIN.getAreaUnit())) .body("[0].total-drainage-area", equalTo(BASIN.getTotalDrainageArea().floatValue())) .body("[0].contributing-drainage-area", equalTo(BASIN.getContributingDrainageArea().floatValue())) diff --git a/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java b/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java index 6e2be0a16..b1a95b377 100644 --- a/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java +++ b/cwms-data-api/src/test/java/fixtures/CwmsDataApiSetupCallback.java @@ -183,10 +183,11 @@ public void beforeAll(ExtensionContext context) throws Exception { // actually assign the new config to the global configuration. just running this here without // the assignment apparently does nothing. RestAssured.config = RestAssured.config() - // we only use doubles - .jsonConfig( - JsonConfig.jsonConfig() - .numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)) + // we only use doubles (NOTE: this is commend out because this config was + // never originally active and will be addressed in a followup) + // .jsonConfig( + // JsonConfig.jsonConfig() + // .numberReturnType(JsonPathConfig.NumberReturnType.DOUBLE)) // our content type processing is a bit more picky now. // I also don't recal seeing any default COntent-Type or Accept header // defaults from browsers that include this much. From a47508f9da66fe58a3adadef5367a23b04ed62fa Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 17:27:46 +0000 Subject: [PATCH 05/13] Fixed additional formatters. --- cwms-data-api/src/main/java/cwms/cda/data/dto/Parameter.java | 4 ++++ cwms-data-api/src/main/java/cwms/cda/data/dto/TimeZoneId.java | 4 ++++ cwms-data-api/src/main/java/cwms/cda/data/dto/Unit.java | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Parameter.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Parameter.java index fbc454c4a..1b695e438 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Parameter.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Parameter.java @@ -12,7 +12,9 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.JsonV2; +import cwms.cda.formatters.xml.XMLv1; import cwms.cda.formatters.xml.XMLv2; import java.util.Objects; @@ -20,7 +22,9 @@ @JsonRootName("parameter") @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.XML}) +@FormattableWith(contentType = Formats.XMLV1, formatter = XMLv1.class) @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) public final class Parameter extends CwmsDTO { private String name; diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeZoneId.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeZoneId.java index 9f68753db..949f16b0b 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeZoneId.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeZoneId.java @@ -13,13 +13,17 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.JsonV2; +import cwms.cda.formatters.xml.XMLv1; import cwms.cda.formatters.xml.XMLv2; import java.time.ZoneId; @JsonRootName("time-zone") @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.XML}) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) +@FormattableWith(contentType = Formats.XMLV1, formatter = XMLv1.class) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) public final class TimeZoneId extends CwmsDTOBase { @JsonProperty(required = true) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Unit.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Unit.java index 7bd1d1cae..0a013d6f5 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Unit.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Unit.java @@ -12,7 +12,9 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.JsonV2; +import cwms.cda.formatters.xml.XMLv1; import cwms.cda.formatters.xml.XMLv2; import java.util.ArrayList; @@ -22,6 +24,8 @@ @JsonRootName("unit") @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.XML}) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) +@FormattableWith(contentType = Formats.XMLV1, formatter = XMLv1.class) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) public final class Unit extends CwmsDTOBase { From aee194fed2e897cda9376a6b416fabe73df1df79 Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 18:04:18 +0000 Subject: [PATCH 06/13] Create 'DUMMY' formatter so assosciations can be made. --- .../java/cwms/cda/data/dto/TimeSeries.java | 2 ++ .../dto/locationlevel/LocationLevels.java | 2 ++ .../cwms/cda/formatters/OutputFormatter.java | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java index ef6856ce0..4201e96e6 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java @@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import cwms.cda.api.enums.VersionType; import cwms.cda.formatters.Formats; +import cwms.cda.formatters.OutputFormatter; import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.json.JsonV2; import cwms.cda.formatters.json.adapters.TimeSeriesRecordSerializer; @@ -35,6 +36,7 @@ @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.XML}) +@FormattableWith(contentType = Formats.CSV, formatter = OutputFormatter.DUMMY.class) public class TimeSeries extends CwmsDTOPaginated { public static final String ZONED_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"; diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/LocationLevels.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/LocationLevels.java index 76fbbd204..a9de210ab 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/LocationLevels.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/locationlevel/LocationLevels.java @@ -32,6 +32,7 @@ import cwms.cda.data.dto.CwmsDTOPaginated; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.JsonV2; import io.swagger.v3.oas.annotations.media.Schema; @@ -42,6 +43,7 @@ @JsonRootName("location-levels") @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) public class LocationLevels extends CwmsDTOPaginated { @JacksonXmlElementWrapper @JacksonXmlProperty(localName = "location-level") diff --git a/cwms-data-api/src/main/java/cwms/cda/formatters/OutputFormatter.java b/cwms-data-api/src/main/java/cwms/cda/formatters/OutputFormatter.java index 8039229b8..c90825453 100644 --- a/cwms-data-api/src/main/java/cwms/cda/formatters/OutputFormatter.java +++ b/cwms-data-api/src/main/java/cwms/cda/formatters/OutputFormatter.java @@ -27,4 +27,25 @@ default List parseContentList(String content, Class dtoList) { + throw new UnsupportedOperationException("Unimplemented method 'format'"); + } + } } From 6034d99e83d344ac4445e6358085487a3eee9764 Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 20:21:19 +0000 Subject: [PATCH 07/13] More format specifier corrections. --- cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java | 1 + cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java index 4c9bb0acc..1e97c95ce 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java @@ -33,6 +33,7 @@ @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.XML}) @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) @FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) +@FormattableWith(contentType = Formats.GEOJSON, formatter = JsonV2.class) public final class Location extends CwmsDTO { @JsonProperty(required = true) private final String name; diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java index 4201e96e6..b26f1b59f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/TimeSeries.java @@ -16,6 +16,7 @@ import cwms.cda.formatters.Formats; import cwms.cda.formatters.OutputFormatter; import cwms.cda.formatters.annotations.FormattableWith; +import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.JsonV2; import cwms.cda.formatters.json.adapters.TimeSeriesRecordSerializer; import cwms.cda.formatters.xml.XMLv2; @@ -36,6 +37,7 @@ @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.XML}) +@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) @FormattableWith(contentType = Formats.CSV, formatter = OutputFormatter.DUMMY.class) public class TimeSeries extends CwmsDTOPaginated { public static final String ZONED_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ'['VV']'"; From dfe6f327baf41dbb2bcb09bcfc8d676829414de7 Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 20:22:23 +0000 Subject: [PATCH 08/13] Use dummy annotation for Ratings Marker. --- .../src/main/java/cwms/cda/data/dto/rating/Ratings.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java index a527ccad6..0c339057f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/Ratings.java @@ -2,11 +2,12 @@ import cwms.cda.data.dto.CwmsDTOBase; import cwms.cda.formatters.Formats; +import cwms.cda.formatters.OutputFormatter; import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.xml.XMLv2; @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.DEFAULT, Formats.XML}) -@FormattableWith(contentType = Formats.JSONV2, formatter = XMLv2.class, aliases = {Formats.JSON}) +@FormattableWith(contentType = Formats.JSONV2, formatter = OutputFormatter.DUMMY.class, aliases = {Formats.JSON}) public class Ratings extends CwmsDTOBase { /** marker interface for ContentType Annotations */ } From e04256c96753da4492a75294092dc8fcb98f62bf Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 21:09:30 +0000 Subject: [PATCH 09/13] Account for new location delete conflict error code. --- .../main/java/cwms/cda/api/LocationController.java | 3 ++- .../cwms/cda/api/LocationControllerTestIT.java | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java b/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java index 3acf92fc9..9c23e178f 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LocationController.java @@ -439,7 +439,8 @@ public void delete(@NotNull Context ctx, @NotNull String locationId) { ctx.status(HttpServletResponse.SC_OK).json(re); } catch (DataAccessException ex) { SQLException cause = ex.getCause(SQLException.class); - if (cause != null && cause.getErrorCode() == 20031) { + if (cause != null && + (cause.getErrorCode() == 20031 || cause.getErrorCode() == 20056)) { throw new DeleteConflictException("Unable to delete requested location: " + locationId + " for office: " + office, cause); } diff --git a/cwms-data-api/src/test/java/cwms/cda/api/LocationControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/LocationControllerTestIT.java index c4ccf7173..8e7786110 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/LocationControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/LocationControllerTestIT.java @@ -54,6 +54,7 @@ import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; import cwms.cda.formatters.json.JsonV1; +import cwms.cda.helpers.DatabaseHelpers.SCHEMA_VERSION; import fixtures.CwmsDataApiSetupCallback; import fixtures.TestAccounts.KeyUser; import io.restassured.filter.log.LogDetail; @@ -1209,6 +1210,15 @@ void testDeleteConflict() throws Exception { String timeseriesId = "Putah_Creek.Elev.Ave.30Minutes.30Minutes.Raw"; createTimeseries(user.getOperatingOffice(), timeseriesId); + String expectedMessage = "Unable to delete requested location: Putah_Creek for office: SPK: ORA-20056: CAN_NOT_DELETE_LOC_2: Can not delete location: " + + "\"Putah_Creek\" because dependent data exists: time series identifiers=1."; + + if (CwmsDataApiSetupCallback.getSchemaVersion() <= SCHEMA_VERSION.V2026_07_16.numeric()) { + expectedMessage = "Unable to delete requested location: " + + "Putah_Creek for office: SPK: ORA-20031: CAN_NOT_DELETE_LOC_1: " + + "Can not delete location: \"Putah_Creek\" because Timeseries Identifiers exist."; + } + // attempt to delete location that is referenced by TS given() .log().ifValidationFails(LogDetail.ALL,true) @@ -1227,9 +1237,7 @@ void testDeleteConflict() throws Exception { .body("source", equalTo("Database")) .body("message", equalTo("Cannot delete this record because it is linked to other data in CWMS")) - .body("details.message", equalTo("Unable to delete requested location: " - + "Putah_Creek for office: SPK: ORA-20031: CAN_NOT_DELETE_LOC_1: " - + "Can not delete location: \"Putah_Creek\" because Timeseries Identifiers exist.")); + .body("details.message", equalTo(expectedMessage)); } @Test From f8cdc5623eb383b6bd2662a3e0d508b9e06e2c3f Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 21:11:14 +0000 Subject: [PATCH 10/13] Revert back to original package-lock.json --- cda-gui/package-lock.json | 55 ++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/cda-gui/package-lock.json b/cda-gui/package-lock.json index ed481ac1d..cc71620c0 100644 --- a/cda-gui/package-lock.json +++ b/cda-gui/package-lock.json @@ -82,7 +82,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1201,6 +1200,9 @@ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1215,6 +1217,9 @@ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1229,6 +1234,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1243,6 +1251,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1257,6 +1268,9 @@ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1271,6 +1285,9 @@ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1285,6 +1302,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1299,6 +1319,9 @@ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1313,6 +1336,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1327,6 +1353,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1341,6 +1370,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1355,6 +1387,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1369,6 +1404,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1502,7 +1540,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.1.tgz", "integrity": "sha512-ZnONUuQKJe1bJMStXUL1s5uKN9FcfC28j5cK+iDZcdSHtUv1wtin1cGc/Oewhf2Oc4eKY7lggtpvT/AbMmhHew==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.101.1" }, @@ -1612,7 +1649,6 @@ "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -1667,7 +1703,6 @@ "resolved": "https://registry.npmjs.org/@usace/groundwork/-/groundwork-3.15.6.tgz", "integrity": "sha512-0Q37NW/wsuST4Y70sPNbR4hfTycyd7MwUIcfIhYvAu1lwxpQTXSAmDPGnwlmCrmHQd4hSTmCB44IHFwyjWHM4A==", "license": "MIT", - "peer": true, "dependencies": { "@headlessui/react": "^2.2.0", "@tailwindcss/forms": "^0.5.9", @@ -1719,7 +1754,6 @@ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2128,7 +2162,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", @@ -2903,7 +2936,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4224,7 +4256,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -5107,7 +5138,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -5372,7 +5402,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5406,7 +5435,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5546,8 +5574,7 @@ "version": "28.1.0", "resolved": "https://registry.npmjs.org/redux-bundler/-/redux-bundler-28.1.0.tgz", "integrity": "sha512-dbq0J0Sm0ctK9ZUh73JR/6J937qzNvkKEHXtHv7p2Hl/9sS+xyRQReWjuju8LHopYFiG+U0Lg9XZM1hPp1WkHg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-bundler-hook": { "version": "1.0.3", @@ -6332,7 +6359,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -6670,7 +6696,6 @@ "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", From 1753e9bfa66ec61cfebb002a86388abb518dab5a Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Wed, 5 Aug 2026 22:22:22 +0000 Subject: [PATCH 11/13] Update next-release to RC with delete loc fix. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 57fd198d9..a9778fbe8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: - env: release image: "ghcr.io/hydrologicengineeringcenter/cwms-database/cwms/database-ready-ora-23.5:26.02.17" - env: next-release - image: "ghcr.io/hydrologicengineeringcenter/cwms-database/cwms/database-ready-ora-23.5:26.07.16-RC01" + image: "ghcr.io/hydrologicengineeringcenter/cwms-database/cwms/database-ready-ora-23.5:26.07.16-RC02" - env: previous image: "ghcr.io/hydrologicengineeringcenter/cwms-database/cwms/database-ready-ora-23.5:25.07.01" name: build and test (jdk ${{matrix.jdk}}, schema ${{matrix.schema.env}}) From d1e5c70cb94877775b9e6132876976480a3ea86d Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Thu, 6 Aug 2026 13:55:35 +0000 Subject: [PATCH 12/13] Minor tweak to RssHandler test to reduce false failures. --- cwms-data-api/src/test/java/cwms/cda/api/rss/RssHandlerIT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cwms-data-api/src/test/java/cwms/cda/api/rss/RssHandlerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/rss/RssHandlerIT.java index 6da2d7043..e4925d1e5 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/rss/RssHandlerIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/rss/RssHandlerIT.java @@ -144,7 +144,7 @@ void test_rss_feed_with_pagination() { String waitStr = nextPage.header("Retry-After"); int wait = waitStr != null && !waitStr.isEmpty() ? Integer.parseInt(waitStr) : 10; try { - Thread.sleep(wait*1000); + Thread.sleep(wait*1000 + 500 /* extra half second just to avoid the best being brittle */); } catch (InterruptedException ex) { LOGGER.atFine().withCause(ex).log("Next query wait was interrupted."); } From caf7286715fd4c38ec0f230aae51e1eee8cba5c3 Mon Sep 17 00:00:00 2001 From: "Michael A. Neilson" Date: Thu, 6 Aug 2026 16:30:49 +0000 Subject: [PATCH 13/13] Set formater to DUMMY to make usages slightly more clear. --- cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java index 1e97c95ce..c2a6c289d 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/Location.java @@ -12,6 +12,7 @@ import cwms.cda.api.enums.Nation; import cwms.cda.data.dto.catalog.LocationAlias; import cwms.cda.formatters.Formats; +import cwms.cda.formatters.OutputFormatter; import cwms.cda.formatters.annotations.FormattableWith; import cwms.cda.formatters.json.JsonV1; import cwms.cda.formatters.json.JsonV2; @@ -33,7 +34,7 @@ @FormattableWith(contentType = Formats.XMLV2, formatter = XMLv2.class, aliases = {Formats.XML}) @FormattableWith(contentType = Formats.JSONV2, formatter = JsonV2.class, aliases = {Formats.DEFAULT, Formats.JSON}) @FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) -@FormattableWith(contentType = Formats.GEOJSON, formatter = JsonV2.class) +@FormattableWith(contentType = Formats.GEOJSON, formatter = OutputFormatter.DUMMY.class) public final class Location extends CwmsDTO { @JsonProperty(required = true) private final String name;