Skip to content

Commit 1d4ecf3

Browse files
committed
CWMSVUE-634 - Implements Access To Water DTO
1 parent dc4e4fe commit 1d4ecf3

File tree

4 files changed

+279
-0
lines changed

4 files changed

+279
-0
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2025 Hydrologic Engineering Center
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package cwms.cda.data.dto;
25+
26+
import com.fasterxml.jackson.annotation.JsonInclude;
27+
import com.fasterxml.jackson.annotation.JsonProperty;
28+
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
29+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
30+
import com.fasterxml.jackson.databind.annotation.JsonNaming;
31+
import cwms.cda.formatters.Formats;
32+
import cwms.cda.formatters.annotations.FormattableWith;
33+
import cwms.cda.formatters.json.JsonV1;
34+
35+
@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class, aliases = {Formats.JSON, Formats.DEFAULT})
36+
@JsonDeserialize(builder = AccessToWaterTimeSeriesIdentifier.Builder.class)
37+
@JsonInclude(JsonInclude.Include.NON_NULL)
38+
@JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class)
39+
public final class AccessToWaterTimeSeriesIdentifier extends CwmsDTO {
40+
@JsonProperty(required = true)
41+
private final String locationId;
42+
@JsonProperty(required = true)
43+
private final TimeSeriesIdentifierDescriptor timeSeriesIdDescriptor;
44+
@JsonProperty(required = true)
45+
private final String tsType;
46+
47+
private AccessToWaterTimeSeriesIdentifier(Builder builder) {
48+
super(builder.officeId);
49+
this.locationId = builder.locationId;
50+
this.timeSeriesIdDescriptor = builder.timeSeriesIdDescriptor;
51+
this.tsType = builder.tsType;
52+
}
53+
54+
public String getLocationId() {
55+
return locationId;
56+
}
57+
58+
public TimeSeriesIdentifierDescriptor getTimeSeriesIdDescriptor() {
59+
return timeSeriesIdDescriptor;
60+
}
61+
62+
public String getTsType() {
63+
return tsType;
64+
}
65+
66+
public static class Builder {
67+
private String locationId;
68+
private TimeSeriesIdentifierDescriptor timeSeriesIdDescriptor;
69+
private String tsType;
70+
private String officeId;
71+
72+
public Builder withLocationId(String locationId) {
73+
this.locationId = locationId;
74+
return this;
75+
}
76+
77+
public Builder withTimeSeriesIdDescriptor(TimeSeriesIdentifierDescriptor timeSeriesIdDescriptor) {
78+
this.timeSeriesIdDescriptor = timeSeriesIdDescriptor;
79+
return this;
80+
}
81+
82+
public Builder withTsType(String tsType) {
83+
this.tsType = tsType;
84+
return this;
85+
}
86+
87+
public Builder withOfficeId(String officeId) {
88+
this.officeId = officeId;
89+
return this;
90+
}
91+
92+
public AccessToWaterTimeSeriesIdentifier build() {
93+
return new AccessToWaterTimeSeriesIdentifier(this);
94+
}
95+
96+
}
97+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2025 Hydrologic Engineering Center
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
package cwms.cda.data.dto;
26+
27+
import java.time.ZoneId;
28+
import static org.junit.jupiter.api.Assertions.*;
29+
30+
import cwms.cda.api.errors.FieldException;
31+
import cwms.cda.formatters.ContentType;
32+
import cwms.cda.formatters.Formats;
33+
import cwms.cda.helpers.DTOMatch;
34+
import java.io.InputStream;
35+
import java.nio.charset.StandardCharsets;
36+
import org.apache.commons.io.IOUtils;
37+
import org.junit.jupiter.api.Test;
38+
39+
final class AccessToWaterTimeSeriesIdentifierTest {
40+
41+
@Test
42+
void createAccessToWaterTimeSeriesData_allFieldsProvided_success() {
43+
TimeSeriesIdentifierDescriptor tsDescriptor = new TimeSeriesIdentifierDescriptor.Builder()
44+
.withTimeSeriesId("VANL.Stage.Inst.15Minutes.0.Ccp-Rev")
45+
.withOfficeId("SWT")
46+
.withActive(true)
47+
.withIntervalOffsetMinutes(0L)
48+
.withZoneId(ZoneId.of("UTC"))
49+
.build();
50+
AccessToWaterTimeSeriesIdentifier item = new AccessToWaterTimeSeriesIdentifier.Builder()
51+
.withOfficeId("SWT")
52+
.withLocationId("VANL")
53+
.withTimeSeriesIdDescriptor(tsDescriptor)
54+
.withTsType("STAGE")
55+
.build();
56+
57+
assertAll(
58+
() -> assertEquals("VANL", item.getLocationId(), "The location ID does not match the provided value"),
59+
() -> DTOMatch.assertMatch(tsDescriptor, item.getTimeSeriesIdDescriptor()),
60+
() -> assertEquals("STAGE", item.getTsType(), "The time series type does not match the provided value")
61+
);
62+
}
63+
64+
@Test
65+
void createAccessToWaterTimeSeriesData_missingField_throwsFieldException() {
66+
TimeSeriesIdentifierDescriptor tsDescriptor = new TimeSeriesIdentifierDescriptor.Builder()
67+
.withTimeSeriesId("VANL.Stage.Inst.15Minutes.0.Ccp-Rev")
68+
.withOfficeId("SWT")
69+
.withActive(true)
70+
.withIntervalOffsetMinutes(0L)
71+
.withZoneId(ZoneId.of("UTC"))
72+
.build();
73+
assertAll(
74+
() -> assertThrows(FieldException.class, () -> {
75+
AccessToWaterTimeSeriesIdentifier item = new AccessToWaterTimeSeriesIdentifier.Builder()
76+
.withOfficeId("SWT")
77+
.withTimeSeriesIdDescriptor(tsDescriptor)
78+
.withTsType("STAGE")
79+
.build();
80+
item.validate();
81+
}, "The validate method should have thrown a FieldException because the location ID is missing"),
82+
83+
() -> assertThrows(FieldException.class, () -> {
84+
AccessToWaterTimeSeriesIdentifier item = new AccessToWaterTimeSeriesIdentifier.Builder()
85+
.withOfficeId("SWT")
86+
.withLocationId("VANL")
87+
.withTsType("STAGE")
88+
.build();
89+
item.validate();
90+
}, "The validate method should have thrown a FieldException because the TimeSeries ID is missing"),
91+
92+
() -> assertThrows(FieldException.class, () -> {
93+
AccessToWaterTimeSeriesIdentifier item = new AccessToWaterTimeSeriesIdentifier.Builder()
94+
.withOfficeId("SWT")
95+
.withLocationId("VANL")
96+
.withTimeSeriesIdDescriptor(tsDescriptor)
97+
.build();
98+
item.validate();
99+
}, "The validate method should have thrown a FieldException because the time series type is missing"),
100+
101+
() -> assertThrows(FieldException.class, () -> {
102+
AccessToWaterTimeSeriesIdentifier item = new AccessToWaterTimeSeriesIdentifier.Builder()
103+
.withLocationId("VANL")
104+
.withTimeSeriesIdDescriptor(tsDescriptor)
105+
.withTsType("STAGE")
106+
.build();
107+
item.validate();
108+
}, "The validate method should have thrown a FieldException because the office id is missing")
109+
);
110+
}
111+
112+
@Test
113+
void createAccessToWaterTimeSeriesData_serialize_roundtrip() {
114+
TimeSeriesIdentifierDescriptor tsDescriptor = new TimeSeriesIdentifierDescriptor.Builder()
115+
.withTimeSeriesId("VANL.Stage.Inst.15Minutes.0.Ccp-Rev")
116+
.withOfficeId("SWT")
117+
.withActive(true)
118+
.withIntervalOffsetMinutes(0L)
119+
.withZoneId(ZoneId.of("UTC"))
120+
.build();
121+
AccessToWaterTimeSeriesIdentifier data = new AccessToWaterTimeSeriesIdentifier.Builder()
122+
.withOfficeId("SWT")
123+
.withLocationId("VANL")
124+
.withTimeSeriesIdDescriptor(tsDescriptor)
125+
.withTsType("STAGE")
126+
.build();
127+
128+
ContentType contentType = new ContentType(Formats.JSON);
129+
String json = Formats.format(contentType, data);
130+
AccessToWaterTimeSeriesIdentifier deserialized = Formats.parseContent(contentType, json, AccessToWaterTimeSeriesIdentifier.class);
131+
DTOMatch.assertMatch(data, deserialized);
132+
}
133+
134+
@Test
135+
void createAccessToWaterTimeSeriesData_deserialize() throws Exception {
136+
TimeSeriesIdentifierDescriptor tsDescriptor = new TimeSeriesIdentifierDescriptor.Builder()
137+
.withTimeSeriesId("VANL.Stage.Inst.15Minutes.0.Ccp-Rev")
138+
.withOfficeId("SWT")
139+
.withActive(true)
140+
.withIntervalOffsetMinutes(0L)
141+
.withZoneId(ZoneId.of("UTC"))
142+
.build();
143+
AccessToWaterTimeSeriesIdentifier expected = new AccessToWaterTimeSeriesIdentifier.Builder()
144+
.withOfficeId("SWT")
145+
.withLocationId("VANL")
146+
.withTimeSeriesIdDescriptor(tsDescriptor)
147+
.withTsType("STAGE")
148+
.build();
149+
150+
InputStream resource = this.getClass().getResourceAsStream("/cwms/cda/data/dto/access_to_water_time_series_data.json");
151+
assertNotNull(resource);
152+
String json = IOUtils.toString(resource, StandardCharsets.UTF_8);
153+
ContentType contentType = new ContentType(Formats.JSON);
154+
AccessToWaterTimeSeriesIdentifier deserialized = Formats.parseContent(contentType, json, AccessToWaterTimeSeriesIdentifier.class);
155+
DTOMatch.assertMatch(expected, deserialized);
156+
}
157+
}

cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424

2525
package cwms.cda.helpers;
2626

27+
import cwms.cda.data.dto.AccessToWaterTimeSeriesIdentifier;
2728
import cwms.cda.data.dto.CwmsIdTimeExtentsEntry;
2829
import cwms.cda.data.dto.TimeExtents;
2930
import cwms.cda.data.dto.TimeSeriesExtents;
31+
import cwms.cda.data.dto.TimeSeriesIdentifierDescriptor;
3032
import cwms.cda.data.dto.location.kind.Lock;
3133
import cwms.cda.data.dto.CwmsDTOBase;
3234
import cwms.cda.data.dto.location.kind.GateChange;
@@ -601,6 +603,17 @@ public static void assertMatch(CwmsIdTimeExtentsEntry first, CwmsIdTimeExtentsEn
601603
);
602604
}
603605

606+
public static void assertMatch(AccessToWaterTimeSeriesIdentifier first, AccessToWaterTimeSeriesIdentifier second) {
607+
assertAll(
608+
() -> assertMatch(first.getTimeSeriesIdDescriptor(), second.getTimeSeriesIdDescriptor()),
609+
() -> assertEquals(first.getTsType(), second.getTsType()),
610+
() -> assertEquals(first.getLocationId(), second.getLocationId())
611+
);
612+
}
613+
614+
public static void assertMatch(TimeSeriesIdentifierDescriptor tsDescriptor, TimeSeriesIdentifierDescriptor timeSeriesIdDescriptor) {
615+
}
616+
604617
@FunctionalInterface
605618
public interface AssertMatchMethod<T>{
606619
void assertMatch(T first, T second);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"office-id":"SWT",
3+
"location-id": "VANL",
4+
"time-series-id-descriptor": {
5+
"office-id": "SWT",
6+
"time-series-id": "VANL.Stage.Inst.15Minutes.0.Ccp-Rev",
7+
"timezone-name": "UTC",
8+
"interval-offset-minutes": 0,
9+
"active": true
10+
},
11+
"ts-type": "STAGE"
12+
}

0 commit comments

Comments
 (0)